Conversation
Anthropic's Messages and Responses APIs require tool_use.id and tool_result.tool_use_id to match ^[a-zA-Z0-9_-]+$. Bifrost forwarded these ids straight through from whatever upstream provider produced them, so replaying a conversation whose tool-call ids came from a Kimi/Gemini-compatible backend (e.g. "functions.Bash:0") through Bifrost to a real Claude model returned a 400 from Anthropic. Add a deterministic, hash-based sanitizer (mirrors the existing Bedrock tool-name aliasing pattern) and wire it into every site that places a caller-supplied id into an outbound Anthropic tool_use, tool_result, server_tool_use, or programmatic-tool-calling caller reference, across both the Chat Completions and Responses API surfaces, and both their streaming and non-streaming paths. Verified end-to-end against the live OpenAI and Anthropic APIs: the same non-conforming tool-call id that previously got a 400 from Anthropic (streaming and non-streaming) now round-trips successfully.
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (2): Last reviewed commit: "fix(anthropic): sanitize empty tool_use ..." | Re-trigger Greptile |
Anthropic's ^[a-zA-Z0-9_-]+$ pattern requires at least one character, so an empty tool_use/tool_result id was being left unchanged by SanitizeAnthropicToolUseID and would still fail validation. Route the empty string through the same hash-based rewrite as non-conforming ids instead of treating it as already-valid. Found by Greptile's automated review on PR maximhq#5096.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughSummary by CodeRabbit
WalkthroughAnthropic tool-use and tool-result identifiers are now deterministically sanitized across Chat and Responses conversions. Shared helpers enforce valid characters, bounded length, stable hashing, and pointer handling, with tests covering these behaviors. ChangesAnthropic tool ID sanitization
Estimated code review effort: 3 (Moderate) | ~20 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 golangci-lint (2.12.2)level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies" Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…set (#5096) * fix(anthropic): sanitize tool_use/tool_result ids to Anthropic's charset Anthropic's Messages and Responses APIs require tool_use.id and tool_result.tool_use_id to match ^[a-zA-Z0-9_-]+$. Bifrost forwarded these ids straight through from whatever upstream provider produced them, so replaying a conversation whose tool-call ids came from a Kimi/Gemini-compatible backend (e.g. "functions.Bash:0") through Bifrost to a real Claude model returned a 400 from Anthropic. Add a deterministic, hash-based sanitizer (mirrors the existing Bedrock tool-name aliasing pattern) and wire it into every site that places a caller-supplied id into an outbound Anthropic tool_use, tool_result, server_tool_use, or programmatic-tool-calling caller reference, across both the Chat Completions and Responses API surfaces, and both their streaming and non-streaming paths. Verified end-to-end against the live OpenAI and Anthropic APIs: the same non-conforming tool-call id that previously got a 400 from Anthropic (streaming and non-streaming) now round-trips successfully. * fix(anthropic): sanitize empty tool_use ids too Anthropic's ^[a-zA-Z0-9_-]+$ pattern requires at least one character, so an empty tool_use/tool_result id was being left unchanged by SanitizeAnthropicToolUseID and would still fail validation. Route the empty string through the same hash-based rewrite as non-conforming ids instead of treating it as already-valid. Found by Greptile's automated review on PR #5096.
…set (maximhq#5096) * fix(anthropic): sanitize tool_use/tool_result ids to Anthropic's charset Anthropic's Messages and Responses APIs require tool_use.id and tool_result.tool_use_id to match ^[a-zA-Z0-9_-]+$. Bifrost forwarded these ids straight through from whatever upstream provider produced them, so replaying a conversation whose tool-call ids came from a Kimi/Gemini-compatible backend (e.g. "functions.Bash:0") through Bifrost to a real Claude model returned a 400 from Anthropic. Add a deterministic, hash-based sanitizer (mirrors the existing Bedrock tool-name aliasing pattern) and wire it into every site that places a caller-supplied id into an outbound Anthropic tool_use, tool_result, server_tool_use, or programmatic-tool-calling caller reference, across both the Chat Completions and Responses API surfaces, and both their streaming and non-streaming paths. Verified end-to-end against the live OpenAI and Anthropic APIs: the same non-conforming tool-call id that previously got a 400 from Anthropic (streaming and non-streaming) now round-trips successfully. * fix(anthropic): sanitize empty tool_use ids too Anthropic's ^[a-zA-Z0-9_-]+$ pattern requires at least one character, so an empty tool_use/tool_result id was being left unchanged by SanitizeAnthropicToolUseID and would still fail validation. Route the empty string through the same hash-based rewrite as non-conforming ids instead of treating it as already-valid. Found by Greptile's automated review on PR maximhq#5096.
…set (maximhq#5096) * fix(anthropic): sanitize tool_use/tool_result ids to Anthropic's charset Anthropic's Messages and Responses APIs require tool_use.id and tool_result.tool_use_id to match ^[a-zA-Z0-9_-]+$. Bifrost forwarded these ids straight through from whatever upstream provider produced them, so replaying a conversation whose tool-call ids came from a Kimi/Gemini-compatible backend (e.g. "functions.Bash:0") through Bifrost to a real Claude model returned a 400 from Anthropic. Add a deterministic, hash-based sanitizer (mirrors the existing Bedrock tool-name aliasing pattern) and wire it into every site that places a caller-supplied id into an outbound Anthropic tool_use, tool_result, server_tool_use, or programmatic-tool-calling caller reference, across both the Chat Completions and Responses API surfaces, and both their streaming and non-streaming paths. Verified end-to-end against the live OpenAI and Anthropic APIs: the same non-conforming tool-call id that previously got a 400 from Anthropic (streaming and non-streaming) now round-trips successfully. * fix(anthropic): sanitize empty tool_use ids too Anthropic's ^[a-zA-Z0-9_-]+$ pattern requires at least one character, so an empty tool_use/tool_result id was being left unchanged by SanitizeAnthropicToolUseID and would still fail validation. Route the empty string through the same hash-based rewrite as non-conforming ids instead of treating it as already-valid. Found by Greptile's automated review on PR maximhq#5096.
Summary
Anthropic requires
tool_use.id/tool_result.tool_use_idto match^[a-zA-Z0-9_-]+$. Bifrost forwarded these ids through unsanitized from any upstream provider, so replaying a conversation whose ids came from a non-conforming backend (e.g.functions.Bash:0) against a real Anthropic model returned a 400, in both streaming and non-streaming requests.Closes #5097
Changes
providerUtils.SanitizeAnthropicToolUseID/...Ptr(core/providers/utils/utils.go), mirroring the existing Bedrock tool-name aliasing pattern. Already-valid ids pass through unchanged.core/providers/anthropic/chat.go,responses.go), including both surfaces' streaming paths.Type of change
Affected areas
How to test
Manually verified live against real OpenAI + Anthropic API keys: replaying a conversation with a non-conforming tool-call id against Anthropic went from a 400 to a 200 on both streaming and non-streaming requests.
Breaking changes
Security considerations
None — reshapes an id already present in the request; no new input accepted.