#16095: fix: remove orphaned tool_result blocks during compaction (#15691)
agents
stale
size: S
Cluster:
Tool Result Compaction Fixes
## Summary
Fixes #15691
After a request is interrupted mid-tool-execution, synthetic `tool_result` blocks are inserted for transcript repair. When context compaction later triggers, the compactor removes assistant messages containing `tool_use` blocks but leaves the corresponding `tool_result` blocks behind. The Anthropic API then rejects all subsequent requests with:
> "unexpected tool_use_id found in tool_result blocks"
## Changes
### `src/agents/pi-embedded-runner/compact.ts`
- Added post-compaction `repairToolUseResultPairing` call immediately after `session.compact()` returns
- When orphaned `tool_result` blocks are detected and removed, logs the cleanup with session context
- Only runs when `transcriptPolicy.repairToolUseResultPairing` is enabled (consistent with pre-compaction behavior)
- Runs before token estimation so post-compaction token counts reflect the repaired state
### `src/agents/session-transcript-repair.e2e.test.ts`
- Added test suite for post-compaction orphaned tool_result scenarios:
- Orphaned tool_results after compaction removes tool_use messages
- Synthetic tool_results from interrupted requests surviving compaction (exact #15691 scenario)
- Mixed valid pairs + orphans (only orphans removed, valid pairs preserved)
- No-op when all tool_use/tool_result pairs are valid
## Root cause
The existing `sanitizeToolUseResultPairing` was only called *before* compaction (after `limitHistoryTurns`). After `session.compact()`, the SDK replaces older messages with a summary but can leave orphaned `tool_result` blocks that no longer have matching `tool_use` blocks. The fix re-runs the same repair logic after compaction completes.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Fixes a bug where orphaned `tool_result` blocks survive context compaction and cause Anthropic API 400 errors (`"unexpected tool_use_id found in tool_result blocks"`). The root cause: after `session.compact()` replaces older messages with a summary, `tool_result` blocks whose matching `tool_use` was in a compacted assistant message become orphaned. The fix re-runs the existing `repairToolUseResultPairing` function immediately after compaction completes, gated behind the same `transcriptPolicy.repairToolUseResultPairing` flag used pre-compaction.
- Adds post-compaction `repairToolUseResultPairing` call in `compact.ts` with appropriate logging and early-exit when no repairs are needed
- Runs before token estimation so post-compaction counts reflect the repaired transcript state
- Adds four well-structured test cases covering the primary failure scenario, synthetic tool results from interrupted requests, mixed valid/orphaned pairs, and a no-op baseline
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge — it applies an existing, well-tested repair function at an additional call site with no behavioral changes to the repair logic itself.
- The change is minimal and well-scoped: it reuses the existing `repairToolUseResultPairing` function (no new logic), is gated behind the same policy flag, only modifies messages when repairs are actually needed, and includes comprehensive test coverage for all relevant scenarios. The single call site for `session.compact()` has been verified.
- No files require special attention.
<sub>Last reviewed commit: 0f88b2e</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#21201: fix(compaction): repair tool_use/tool_result pairing after compaction
by ruslansychov-git · 2026-02-19
89.4%
#13062: fix: remove orphaned tool_result blocks from user message content d...
by trevorgordon981 · 2026-02-10
86.7%
#4852: fix(agents): sanitize tool pairing after compaction and history tru...
by lailoo · 2026-01-30
86.1%
#12487: fix(agents): strip orphaned tool_result when tool_use is sanitized ...
by skylarkoo7 · 2026-02-09
84.8%
#11825: fix: keep tool_use/tool_result pairs together during session compac...
by C31gordon · 2026-02-08
83.8%
#4844: fix(agents): skip error/aborted assistant messages in transcript re...
by lailoo · 2026-01-30
83.0%
#21195: fix: suppress orphaned tool_use/tool_result errors after session co...
by ruslansychov-git · 2026-02-19
83.0%
#8345: fix: prevent synthetic error repair from creating tool_result for d...
by vishaltandale00 · 2026-02-03
82.8%
#6687: fix(session-repair): strip malformed tool_use blocks to prevent per...
by NSEvent · 2026-02-01
82.8%
#14328: fix: strip incomplete tool_use blocks from errored/aborted messages...
by Kropiunig · 2026-02-12
81.7%