#23698: fix: sanitize tool call IDs in agent loop for Mistral strict9 format (#23595)
agents
size: XS
trusted-contributor
Cluster:
Tool Call ID Sanitization
## Summary
Fixes #23595 — Mistral tool calls fail with HTTP 400 because tool call IDs contain underscores and don't match the required `[a-zA-Z0-9]{9}` format.
## Root Cause
The existing `sanitizeToolCallIdsForCloudCodeAssist` mechanism only ran on **historical messages** at attempt start via `sanitizeSessionHistory`. However, pi-agent-core's agent loop internally cycles through tool call → tool result without going through that sanitization path. This means tool call IDs generated by the model (or by OpenClaw) during the current turn were sent back to Mistral unsanitized.
Additionally, `run.ts` generated client tool call IDs using `call_${Date.now()}` which contains underscores and has variable length — incompatible with Mistral's strict format.
## Changes
1. **Wrap `streamFn` for tool call ID sanitization** (`attempt.ts`)
- Follows the same pattern as `dropThinkingBlocks` — wraps the stream function so every outbound request sees sanitized tool call IDs
- Only activates when `transcriptPolicy.sanitizeToolCallIds` is true (Mistral, Google, Anthropic)
- Uses the correct mode (`strict9` for Mistral, `strict` for others)
2. **Fix `pendingToolCalls` ID generation** (`run.ts`)
- Replace `call_${Date.now()}` with `randomBytes(5).toString('hex').slice(0, 9)` — 9-char alphanumeric, compatible with all providers
3. **Add Mistral error pattern** (`errors.ts`)
- Add `/tool call id was.*must be/i` to `ERROR_PATTERNS.format` so the error is correctly classified for retry/rotation
## Testing
- `tool-call-id.test.ts` — 7/7 ✅
- `sanitize-session-history.test.ts` — 20/20 ✅
- `transcript-policy.test.ts` + `policy.test.ts` — 6/6 ✅
- `auth-profile-rotation.test.ts` — 11/11 ✅
- `isbillingerrormessage.test.ts` — 30/30 ✅
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR fixes Mistral API failures caused by tool call IDs not matching the required `[a-zA-Z0-9]{9}` format. The fix wraps the stream function in the agent loop to sanitize tool call IDs on every outbound request, not just historical messages. It also replaces the underscore-containing `call_${Date.now()}` ID generation with `randomBytes(5).toString('hex').slice(0, 9)` to ensure 9-character alphanumeric IDs. The error pattern `/tool call id was.*must be/i` is added for proper retry/rotation classification. The implementation follows the existing `dropThinkingBlocks` wrapper pattern and only activates for providers with `transcriptPolicy.sanitizeToolCallIds` enabled.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk
- The fix is well-implemented and follows established patterns in the codebase. All tests pass (7/7 for tool-call-id.test.ts, 20/20 for sanitize-session-history.test.ts). The changes are narrowly scoped to fixing the specific Mistral tool call ID issue, with proper conditional activation and appropriate fallback behavior.
- No files require special attention
<sub>Last reviewed commit: 64193fc</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#23740: fix(run): generate Mistral-compatible tool call IDs (9-char alphanu...
by mrx-arafat · 2026-02-22
89.5%
#12608: fix: sanitize client tool call IDs per provider requirements
by piyushhhxyz · 2026-02-09
84.6%
#13831: fix(agents): include Anthropic in tool call ID sanitization
by lailoo · 2026-02-11
82.5%
#12487: fix(agents): strip orphaned tool_result when tool_use is sanitized ...
by skylarkoo7 · 2026-02-09
81.9%
#12812: fix(transcript-policy): sanitize tool call IDs for all non-OpenAI p...
by justin-nevins · 2026-02-09
81.3%
#19094: Fix empty tool_call_id and function names in provider transcript pa...
by yxshee · 2026-02-17
81.1%
#13976: fix(anthropic): include Anthropic in tool call ID sanitization
by omair445 · 2026-02-11
80.9%
#21166: fix(agents): sanitize tool names in session transcript repair (#8595)
by dinakars777 · 2026-02-19
80.3%
#3647: fix: sanitize tool arguments in session history
by nhangen · 2026-01-29
79.9%
#15649: fix: sanitize tool_use IDs on session write path
by aldoeliacim · 2026-02-13
79.6%