Skip to content

fix(sessions): truncate at incomplete tool calls instead of synthetic results#3565

Closed
kiranjd wants to merge 1 commit intoopenclaw:mainfrom
kiranjd:fix/session-truncate-incomplete-tool-calls
Closed

fix(sessions): truncate at incomplete tool calls instead of synthetic results#3565
kiranjd wants to merge 1 commit intoopenclaw:mainfrom
kiranjd:fix/session-truncate-incomplete-tool-calls

Conversation

@kiranjd
Copy link
Contributor

@kiranjd kiranjd commented Jan 28, 2026

Summary

When session history contains an assistant message with tool calls but missing results (due to branching, interruption, or crash), the API rejects the request with errors like:

"an assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'"

This change makes agents recover automatically instead of getting stuck.

What users experience

Before (broken)

  1. User messages agent
  2. Agent is stuck, returns errors, won't respond
  3. Only fix: manually delete entire conversation history
  4. User loses everything

After (fixed)

  1. User messages agent
  2. Agent responds normally
  3. Agent may have forgotten one thing from earlier (the broken part)
  4. User asks again if needed, conversation continues

Technical details

Root cause

Session files use a tree structure with branching. When linearizing the tree for API calls:

  1. Tool calls and their results can end up on different branches
  2. Linearization picks one path, potentially leaving tool results orphaned
  3. API rejects the malformed history

Previous approach (fragile)

  • Scan transcript, try to re-associate orphaned tool results
  • Insert synthetic error results for missing tool calls
  • Drop orphaned results

Problems:

  • Complex repair logic with many edge cases
  • Synthetic error results confuse the conversation
  • Still failed in some scenarios

New approach (simple)

  • Detect the first assistant message with incomplete tool calls
  • Truncate history before that message
  • Always produces valid history (just shorter)
  • Log truncation details for debugging

Code changes

  • session-transcript-repair.ts: New findFirstIncompleteToolCallIndex() function, truncation logic
  • google.ts: Log when truncation occurs for monitoring
  • Tests updated to verify new behavior

Alternatives considered

Approach Why rejected
Insert synthetic error results Confusing conversation state, fragile
Show error, ask user to fix Users shouldn't need to understand session internals
Delete entire conversation Too much data loss
Prevent all interruptions Impossible (network issues, crashes, user edits)

Testing

  • Unit tests updated and passing
  • Lint passing
  • Build passing

🤖 Generated with Claude Code

Greptile Overview

Greptile Summary

This PR changes session transcript recovery to avoid provider rejections when an assistant tool-call turn lacks matching tool results. Instead of inserting synthetic error tool results, repairToolUseResultPairing now detects the first assistant message with missing tool results and truncates history before it, then reorders any in-span tool results and drops duplicates/orphans. sanitizeSessionHistory is updated to use the new repair report and log when truncation occurs, and tests were updated to cover the new truncation behavior.

Confidence Score: 3/5

  • Mostly safe to merge, but the new truncation detection may miss the branch/orphan scenario it is meant to fix.
  • Core change is localized and tests cover the new truncation behavior, but findFirstIncompleteToolCallIndex treats any matching toolResult anywhere in the transcript as satisfying a tool call, which can prevent truncation when results exist off-path (the described root cause). Logging change is low risk.
  • src/agents/session-transcript-repair.ts

Context used:

  • Context from dashboard - CLAUDE.md (source)
  • Context from dashboard - AGENTS.md (source)

… synthetic results

When session history contains an assistant message with tool calls but
missing results (e.g., due to branching, interruption, or crash), the
API rejects the request. Previously, we tried to repair this by
inserting synthetic error results, but this approach was fragile and
could lead to confusing conversation states.

New approach: truncate history before the first incomplete tool call
sequence. This is simpler and always produces valid history:

- Agent continues working (just with less context)
- No confusing synthetic error results
- User may need to repeat their last request

The truncation is logged with details about which tool call IDs were
missing, helping track and diagnose the root cause.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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.

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 3, 2026

Additional Comments (2)

src/agents/session-transcript-repair.ts
[P1] Truncation check can false-positive due to global toolResult scan

findFirstIncompleteToolCallIndex precomputes allResultIds by scanning all tool results anywhere in the transcript (indexAllToolResultIds). That means an assistant tool call will be considered “complete” if a matching toolResult exists later on a different branch/turn, even if it won’t be included after linearization (the exact failure mode described in the PR). In that scenario, this function won’t truncate and the provider will still reject the request.

This is especially likely when the missing results are actually “orphaned on another branch” rather than truly absent. Consider checking for tool results only within the linearized path/after the call turn (or the same span the reordering logic already scans) instead of globally.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/session-transcript-repair.ts
Line: 80:83

Comment:
[P1] Truncation check can false-positive due to global toolResult scan

`findFirstIncompleteToolCallIndex` precomputes `allResultIds` by scanning *all* tool results anywhere in the transcript (`indexAllToolResultIds`). That means an assistant tool call will be considered “complete” if a matching `toolResult` exists later on a different branch/turn, even if it won’t be included after linearization (the exact failure mode described in the PR). In that scenario, this function won’t truncate and the provider will still reject the request.

This is especially likely when the missing results are actually “orphaned on another branch” rather than truly absent. Consider checking for tool results only within the linearized path/after the call turn (or the same span the reordering logic already scans) instead of globally.


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

src/agents/pi-embedded-runner/google.ts
[P2] Log may include untrusted tool call IDs and get very large

The truncation warning logs missingToolCallIds.join(", ") inline. If tool call IDs can be influenced by user/session contents (or just be numerous), this can produce noisy/oversized logs and make parsing harder. Consider logging them as structured data (array field) and/or truncating the list/count (e.g., first N ids + total) to keep logs bounded.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-runner/google.ts
Line: 341:348

Comment:
[P2] Log may include untrusted tool call IDs and get very large

The truncation warning logs `missingToolCallIds.join(", ")` inline. If tool call IDs can be influenced by user/session contents (or just be numerous), this can produce noisy/oversized logs and make parsing harder. Consider logging them as structured data (array field) and/or truncating the list/count (e.g., first N ids + total) to keep logs bounded.


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

@kiranjd kiranjd closed this Feb 15, 2026
@kiranjd kiranjd deleted the fix/session-truncate-incomplete-tool-calls branch February 15, 2026 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant