#22011: fix(transcript): drop empty toolCallId toolResults during persistence + repair
agents
size: S
Cluster:
Error Handling in Agent Tools
## Summary
Prevent malformed `toolResult` entries with empty/missing `toolCallId` from being persisted or retained during transcript repair.
## Problem
Issue #21985 reports sessions becoming poisoned by phantom/orphan `toolResult` entries with blank IDs. Once these land in transcript history, strict providers can reject subsequent requests because `toolResult` pairing no longer matches any valid `tool_use` id.
## Solution
1. **Guard at write time** (`session-tool-result-guard.ts`)
- Drop `toolResult` messages when `extractToolResultId(...)` returns empty/missing ID.
- This prevents malformed tool results from ever entering persisted session history.
2. **Repair at sanitize time** (`session-transcript-repair.ts`)
- Drop `toolResult` entries with empty/missing IDs during `repairToolUseResultPairing`.
- This cleans existing corrupted transcripts defensively.
3. **Regression tests**
- Added test: drops empty `toolCallId` in guard flow.
- Added test: drops empty `toolCallId` in transcript repair flow.
## Testing
- `pnpm check` ✅
- `pnpm build` ✅
- `pnpm exec vitest run --config vitest.e2e.config.ts src/agents/session-transcript-repair.e2e.test.ts src/agents/session-tool-result-guard.e2e.test.ts src/agents/session-tool-result-guard.tool-result-persist-hook.e2e.test.ts` ✅ (30/30)
## Local Validation
- `pnpm build` ✅
- `pnpm check` ✅
- `pnpm test` ➖ (targeted e2e suites above; full matrix deferred to CI)
## AI-Assisted
This PR was developed with AI assistance.
Fixes #21985
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Prevents malformed `toolResult` entries with empty/missing `toolCallId` from being persisted to session transcripts or retained during repair, addressing session poisoning issues with strict providers (Anthropic, MiniMax, Cloud Code Assist).
- Added guard at write time in `session-tool-result-guard.ts` to drop toolResults when `extractToolResultId()` returns empty/null
- Added repair logic in `session-transcript-repair.ts` to drop empty-ID toolResults during `repairToolUseResultPairing()`
- Both changes include detailed comments referencing issue #21985
- Regression tests added for both guard and repair flows
<h3>Confidence Score: 5/5</h3>
- Safe to merge with no issues found
- The changes are minimal, focused, and well-tested. The implementation correctly handles the edge case of empty toolCallIds at both persistence and repair time. The code follows existing patterns, includes clear comments, and has comprehensive test coverage. No logical errors, security issues, or side effects identified.
- No files require special attention
<sub>Last reviewed commit: 764c1fa</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#19094: Fix empty tool_call_id and function names in provider transcript pa...
by yxshee · 2026-02-17
86.0%
#12487: fix(agents): strip orphaned tool_result when tool_use is sanitized ...
by skylarkoo7 · 2026-02-09
85.2%
#4844: fix(agents): skip error/aborted assistant messages in transcript re...
by lailoo · 2026-01-30
85.1%
#15143: fix(transcript-repair): validate tool call id and name to prevent G...
by GreyC · 2026-02-13
84.9%
#3622: fix(agents): drop orphan tool results
by mickobizzle · 2026-01-28
83.2%
#15509: fix(session): drop tool_use blocks with empty or missing name
by aldoeliacim · 2026-02-13
83.1%
#4852: fix(agents): sanitize tool pairing after compaction and history tru...
by lailoo · 2026-01-30
83.0%
#6687: fix(session-repair): strip malformed tool_use blocks to prevent per...
by NSEvent · 2026-02-01
82.4%
#15649: fix: sanitize tool_use IDs on session write path
by aldoeliacim · 2026-02-13
82.4%
#8345: fix: prevent synthetic error repair from creating tool_result for d...
by vishaltandale00 · 2026-02-03
82.1%