fix: deduplicate tool_use IDs and enable sanitization for Anthropic#4700
fix: deduplicate tool_use IDs and enable sanitization for Anthropic#4700marcelomar21 wants to merge 4 commits intoopenclaw:mainfrom
Conversation
| // Check for duplicate tool_use IDs and remap them if necessary | ||
| const idRemapping = new Map<string, string>(); | ||
| let assistantNeedsRewrite = false; |
There was a problem hiding this comment.
[P0] Deduplication remapping can drop tool calls/results when an assistant contains duplicate IDs in the same message.
Because idRemapping is keyed by the original ID, multiple tool calls with the same call.id in a single assistant turn will all map to the same new ID (the last one wins). Then effectiveToolCalls ends up with repeated id values, and pushToolResult will drop the second result as a duplicate (since it tracks seenToolResultIds by toolCallId). This can silently lose tool results and/or synthesize a missing result for the second call.
Scenario: assistant has two tool calls both id: "call_1", followed by two toolResults with toolCallId: "call_1" and different content. After rewrite, both tool calls become call_1_2, and the second toolResult is dropped as a duplicate.
Consider disambiguating duplicate IDs within a single assistant message (e.g., track per-call occurrence index rather than mapping by ID), or reject/repair at extraction time.
Also appears in: src/agents/session-transcript-repair.test.ts lacks coverage for same-message duplicate IDs.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/session-transcript-repair.ts
Line: 156:158
Comment:
[P0] Deduplication remapping can drop tool calls/results when an assistant contains duplicate IDs in the *same* message.
Because `idRemapping` is keyed by the original ID, multiple tool calls with the same `call.id` in a single assistant turn will all map to the same new ID (the last one wins). Then `effectiveToolCalls` ends up with repeated `id` values, and `pushToolResult` will drop the second result as a duplicate (since it tracks `seenToolResultIds` by `toolCallId`). This can silently lose tool results and/or synthesize a missing result for the second call.
Scenario: assistant has two tool calls both `id: "call_1"`, followed by two toolResults with `toolCallId: "call_1"` and different content. After rewrite, both tool calls become `call_1_2`, and the second toolResult is dropped as a duplicate.
Consider disambiguating duplicate IDs *within a single assistant message* (e.g., track per-call occurrence index rather than mapping by ID), or reject/repair at extraction time.
Also appears in: `src/agents/session-transcript-repair.test.ts` lacks coverage for same-message duplicate IDs.
How can I resolve this? If you propose a fix, please make it concise.Anthropic API rejects requests with duplicate tool_use IDs across messages. This can happen when: 1. Session transcripts have multiple assistant messages with the same tool_use ID 2. IDs contain special characters that weren't being sanitized for Anthropic Changes: - Add deduplication logic in repairToolUseResultPairing() to detect and rename duplicate tool_use IDs in assistant messages (e.g., call_1 -> call_1_2) - Update corresponding toolResult IDs to match the remapped tool_use IDs - Enable sanitizeToolCallIds for Anthropic provider (was only Google/Mistral) - Add tests for deduplication scenarios Fixes error: "messages.X.content.Y: tool_use ids must be unique"
Previously, idRemapping used a Map<originalId, newId> which caused all tool calls with the same ID in a single message to map to the last generated newId. This resulted in lost tool results for earlier occurrences. Changes: - Track remappings by block index instead of ID - Collect multiple toolResults per ID in an array - Consume results in order using a counter per originalId - Add tests for same-message duplicate ID scenarios Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
7930ec3 to
820e0ac
Compare
Addressed: Duplicate IDs within same assistant messageThanks for the excellent catch @greptileai! The issue was real - using FixChanged the remapping strategy from ID-based to block-index-based:
Tests added
Both scenarios now pass correctly with unique IDs ( |
|
Perfect! Your block-index-based fix elegantly solves the collision problem I identified. The key improvements are spot on: What makes this solution robust:
Edge cases now handled: The test coverage is thorough and the solution maintains the existing transcript repair pipeline while satisfying Anthropic's unique ID requirement. This should resolve the Ready to ship! 🚢 |
bfc1ccb to
f92900f
Compare
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
|
This pull request has been automatically marked as stale due to inactivity. |
|
Closing due to inactivity. |
Summary
Fixes Anthropic API rejection error:
messages.X.content.Y: tool_use ids must be uniqueThis issue occurs when:
Changes
repairToolUseResultPairing()to detect and rename duplicate tool_use IDs in assistant messages (e.g.,call_1→call_1_2)sanitizeToolCallIdsfor Anthropic provider (previously only Google/Mistral)Test plan
session-transcript-repair.test.ts)Greptile Overview
Greptile Summary
This PR strengthens transcript sanitization/repair for Anthropic-compatible requests by (1) enabling
sanitizeToolCallIdsfor Anthropic in the transcript policy and (2) extendingrepairToolUseResultPairing()to deduplicate duplicate assistant tool_use IDs across a session and remap the corresponding toolResult IDs. Tests were updated/added to cover the new Anthropic sanitize flag and a basic duplicate-ID-across-messages scenario.The changes fit into the existing transcript hygiene pipeline (
sanitizeSessionHistoryinsrc/agents/pi-embedded-runner/google.ts), where messages are first sanitized (including tool call IDs) and then repaired to ensure strict toolCall/toolResult ordering expected by providers like Anthropic.Confidence Score: 3/5
pushToolResultto treat later results as duplicates and drop them. That edge case could affect real transcripts if a retry duplicates blocks inside one message.(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!
Context used:
dashboard- CLAUDE.md (source)dashboard- AGENTS.md (source)