#8345: fix: prevent synthetic error repair from creating tool_result for dropped tool_use
agents
stale
Cluster:
Error Handling in Agent Tools
## Summary
This PR fixes issue #8264 where OpenClaw's transcript repair mechanism creates malformed tool_use/tool_result pairs, causing permanent session corruption.
## Problem
When a tool_use block has no input/arguments:
1. `sanitizeToolCallInputs()` drops it from the assistant message
2. `sanitizeToolUseResultPairing()` then runs and creates a synthetic error result for the now-missing tool_use
3. Anthropic rejects the transcript with:
```
unexpected tool_use_id found in tool_result blocks: toolu_XXX.
Each tool_result block must have a corresponding tool_use block
in the previous message.
```
4. Session becomes permanently corrupted - even `openclaw session reset` fails
5. Only fix: manually delete the session file
## Root Cause
The two repair functions run in sequence (as seen in `google.ts` lines 352-354):
```typescript
const sanitizedToolCalls = sanitizeToolCallInputs(sanitizedThinking);
const repairedTools = policy.repairToolUseResultPairing
? sanitizeToolUseResultPairing(sanitizedToolCalls)
: sanitizedToolCalls;
```
**The bug:** `extractToolCallsFromAssistant()` was extracting ALL tool calls, but `repairToolCallInputs()` drops tool calls without input/arguments. When both repairs run, synthetic results get created for tool_use blocks that no longer exist in the message.
## Solution
Modified `extractToolCallsFromAssistant()` to skip tool calls that don't have input/arguments, matching the logic in `repairToolCallInputs()`. This ensures:
- Synthetic results are only created for tool calls that will survive the full repair pipeline
- No ID mismatches between tool_use and tool_result blocks
- Sessions don't get corrupted
## Changes
1. **src/agents/session-transcript-repair.ts**
- Added check in `extractToolCallsFromAssistant()` to skip tool calls without input/arguments
- Uses existing `isToolCallBlock()` and `hasToolCallInput()` helper functions
- 4 lines added with clear comment referencing #8264
2. **src/agents/session-transcript-repair.test.ts**
- Added comprehensive test case that reproduces the bug scenario
- Verifies the fix: runs both repairs in sequence and ensures no orphan synthetic results
- Tests the exact production code path from `google.ts`
## Testing
Added test case "does not create synthetic results for tool calls without input (issue #8264)" that:
- Creates an assistant message with one valid tool call (has input) and one invalid (no input)
- Runs both `sanitizeToolCallInputs()` and `sanitizeToolUseResultPairing()` in sequence
- Verifies only ONE synthetic result is created (for the valid tool call)
- Confirms no synthetic result for the dropped tool call (which would cause Anthropic error)
## Impact
- **Fixes critical bug:** Prevents permanent session corruption
- **Minimal change:** Only 4 lines of logic added, reuses existing helper functions
- **Backward compatible:** Doesn't change behavior for valid tool calls
- **Well-tested:** New test covers the exact bug scenario
Fixes #8264
---
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR updates transcript repair so that tool calls without `input`/`arguments` are excluded from `extractToolCallsFromAssistant()`, preventing `sanitizeToolUseResultPairing()` from generating synthetic `toolResult` entries for tool calls that `sanitizeToolCallInputs()` will later drop (fixes the tool_use/tool_result ID mismatch seen in #8264). It also adds a regression test that runs both sanitizers in the production order to ensure only surviving tool calls receive synthetic results.
<h3>Confidence Score: 4/5</h3>
- This PR looks safe to merge and addresses a real transcript-corruption edge case with targeted logic and a regression test.
- Change is small and localized (filtering malformed tool call blocks during extraction) and the new test covers the reported failure mode by exercising the real sanitizer ordering. Main remaining concern is behavioral change if `sanitizeToolUseResultPairing()` is used without `sanitizeToolCallInputs()` first, since malformed tool calls would now be ignored by pairing repair.
- src/agents/session-transcript-repair.ts (behavioral contract between sanitizers)
<!-- greptile_other_comments_section -->
**Context used:**
- Context from `dashboard` - CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=fd949e91-5c3a-4ab5-90a1-cbe184fd6ce8))
- Context from `dashboard` - AGENTS.md ([source](https://app.greptile.com/review/custom-context?memory=0d0c8278-ef8e-4d6c-ab21-f5527e322f13))
<!-- /greptile_comment -->
Most Similar PRs
#6687: fix(session-repair): strip malformed tool_use blocks to prevent per...
by NSEvent · 2026-02-01
91.6%
#8312: fix: add logging and markers for tool result repair
by ekson73 · 2026-02-03
90.1%
#12487: fix(agents): strip orphaned tool_result when tool_use is sanitized ...
by skylarkoo7 · 2026-02-09
89.6%
#4700: fix: deduplicate tool_use IDs and enable sanitization for Anthropic
by marcelomar21 · 2026-01-30
88.7%
#4844: fix(agents): skip error/aborted assistant messages in transcript re...
by lailoo · 2026-01-30
88.0%
#14328: fix: strip incomplete tool_use blocks from errored/aborted messages...
by Kropiunig · 2026-02-12
87.2%
#15050: fix: transcript corruption resilience — strip aborted tool_use bloc...
by yashchitneni · 2026-02-12
86.9%
#8270: fix: support snake_case 'tool_use' in transcript repair (#8264)
by heliosarchitect · 2026-02-03
86.8%
#3880: fix: drop assistant messages with stopReason 'error' to avoid orpha...
by SalimBinYousuf1 · 2026-01-29
86.4%
#3647: fix: sanitize tool arguments in session history
by nhangen · 2026-01-29
86.2%