Skip to content

fix(agent): sanitize messages after orphan user repair#4009

Closed
drag88 wants to merge 1 commit intoopenclaw:mainfrom
drag88:fix/sanitize-orphan-user-repair
Closed

fix(agent): sanitize messages after orphan user repair#4009
drag88 wants to merge 1 commit intoopenclaw:mainfrom
drag88:fix/sanitize-orphan-user-repair

Conversation

@drag88
Copy link

@drag88 drag88 commented Jan 29, 2026

Summary

When repairing orphaned trailing user messages, buildSessionContext() was called but messages bypassed the sanitization pipeline. This could cause tool_result blocks without matching tool_use blocks, leading to Anthropic API rejecting requests with "unexpected tool_use_id" errors.

The fix: Apply the full sanitization pipeline (sanitizeSessionHistory, validateGeminiTurns, validateAnthropicTurns, limitHistoryTurns) to rebuilt messages, matching the behavior of the initial sanitization at lines 502-524.

Test Plan

  • Tested locally with WhatsApp bot that was experiencing this error
  • Confirmed the error no longer occurs after the fix
  • Linter passes (npm run lint)

AI-Assisted PR 🤖

  • AI-assisted: This fix was developed with Claude (Opus 4.5)
  • Testing level: Fully tested - reproduced the bug, applied fix, verified resolution
  • Understanding: The fix ensures that when messages are rebuilt via buildSessionContext() after repairing orphaned user messages, they go through the same sanitization pipeline as the initial message processing, preventing tool_use/tool_result pairing mismatches

Context

The bug was discovered when a WhatsApp bot session had an aborted request that created a branch in the conversation tree. When linearized, a toolResult was included without its corresponding tool_use block, causing the Anthropic API to reject the request.

Greptile Overview

Greptile Summary

This change fixes a subtle session-repair edge case in runEmbeddedAttempt() where rebuilding session context after repairing an orphaned trailing user message would bypass the normal transcript sanitization/validation pipeline. The new code runs rebuilt messages through sanitizeSessionHistory(), optional turn validators (validateGeminiTurns / validateAnthropicTurns), and limitHistoryTurns() before calling activeSession.agent.replaceMessages(), aligning behavior with the initial session sanitization done earlier in the function.

Overall this should prevent malformed tool_use/tool_result pairings (e.g., orphaned tool_result blocks) from reaching provider APIs like Anthropic and causing request rejections ("unexpected tool_use_id").

Confidence Score: 4/5

  • This PR looks safe to merge and addresses a real failure mode, with low behavioral risk outside the repair path.
  • The change is localized and mirrors an existing sanitization/validation pipeline already used earlier in the same function, which reduces the chance of introducing new transcript-format bugs. Main remaining concern is maintainability: the pipeline is duplicated in two places and could drift over time, but that’s not an immediate functional correctness issue.
  • src/agents/pi-embedded-runner/run/attempt.ts (keep the duplicated sanitization pipeline in sync with the earlier one)

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

When repairing orphaned trailing user messages, buildSessionContext()
was called but messages bypassed the sanitization pipeline. This could
cause tool_result blocks without matching tool_use blocks, leading to
Anthropic API "unexpected tool_use_id" errors.

Apply full sanitization (sanitizeSessionHistory, validateTurns,
limitHistoryTurns) to rebuilt messages.
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 3, 2026

Additional Comments (1)

src/agents/pi-embedded-runner/run/attempt.ts
[P2] Consider reusing the existing sanitization/validation/limit pipeline to avoid drift.

This block duplicates the earlier sanitize/validate/limit logic at attempt.ts:502-525, so future edits (e.g., adding a new validation step or recording cacheTrace stages) could update one path but not the other. A small local helper (or extracting the existing pipeline into a function) would keep both call sites in sync.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-runner/run/attempt.ts
Line: 714:733

Comment:
[P2] Consider reusing the existing sanitization/validation/limit pipeline to avoid drift.

This block duplicates the earlier sanitize/validate/limit logic at `attempt.ts:502-525`, so future edits (e.g., adding a new validation step or recording cacheTrace stages) could update one path but not the other. A small local helper (or extracting the existing pipeline into a function) would keep both call sites in sync.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@mudrii

This comment was marked as spam.

@mudrii

This comment was marked as spam.

@openclaw-barnacle
Copy link

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle bot added the stale Marked as stale due to inactivity label Mar 7, 2026
@openclaw-barnacle
Copy link

Closing due to inactivity.
If you believe this PR should be revived, post in #pr-thunderdome-dangerzone on Discord to talk to a maintainer.
That channel is the escape hatch for high-quality PRs that get auto-closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling stale Marked as stale due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants