Skip to content

fix(anthropic): sanitize tool_use/tool_result ids to Anthropic's charset - #5096

Merged
akshaydeo merged 2 commits into
maximhq:devfrom
Shaik-Sirajuddin:worktree-fix+anthropic-tool-id-sanitize
Jul 12, 2026
Merged

akshaydeo merged 2 commits into
maximhq:devfrom
Shaik-Sirajuddin:worktree-fix+anthropic-tool-id-sanitize

Conversation

@Shaik-Sirajuddin

@Shaik-Sirajuddin Shaik-Sirajuddin commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Anthropic requires tool_use.id/tool_result.tool_use_id to 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

  • New deterministic, hash-based providerUtils.SanitizeAnthropicToolUseID/...Ptr (core/providers/utils/utils.go), mirroring the existing Bedrock tool-name aliasing pattern. Already-valid ids pass through unchanged.
  • Wired into every site that writes a caller-supplied id into an outbound Anthropic tool_use/tool_result/server_tool_use/mcp/caller block, across the Chat Completions and Responses API surfaces (core/providers/anthropic/chat.go, responses.go), including both surfaces' streaming paths.
  • Unit tests for determinism, charset validity, collision avoidance, and length capping.

Type of change

  • Bug fix

Affected areas

  • Core (Go)
  • Providers/Integrations

How to test

cd core && go test ./providers/anthropic/... ./providers/utils/...

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

  • No

Security considerations

None — reshapes an id already present in the request; no new input accepted.

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.
@greptile-apps

greptile-apps Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
core/providers/utils/utils.go Adds the Anthropic ID sanitizer and pointer helper, including the empty-ID rewrite.
core/providers/anthropic/chat.go Applies sanitized IDs to Anthropic Chat tool-use and tool-result conversion paths.
core/providers/anthropic/responses.go Applies sanitized IDs to Anthropic Responses tool-use, tool-result, server-tool, MCP, and caller fields.
core/providers/utils/utils_test.go Adds tests for the sanitizer and pointer wrapper behavior.

Reviews (2): Last reviewed commit: "fix(anthropic): sanitize empty tool_use ..." | Re-trigger Greptile

Comment thread core/providers/utils/utils.go Outdated
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.
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 77804820-4034-437f-a2bc-36e224a65587

📥 Commits

Reviewing files that changed from the base of the PR and between 3fbd9a4 and 21e7d7a.

📒 Files selected for processing (4)
  • core/providers/anthropic/chat.go
  • core/providers/anthropic/responses.go
  • core/providers/utils/utils.go
  • core/providers/utils/utils_test.go

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved compatibility with Anthropic by sanitizing tool-use identifiers in requests, responses, and streaming events.
    • Prevented invalid or overly long tool identifiers from causing tool-call and tool-result processing issues.
    • Ensured consistent identifiers across related tool-use events.
  • Tests

    • Added coverage for identifier validity, length limits, determinism, uniqueness, and nullable values.

Walkthrough

Anthropic 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.

Changes

Anthropic tool ID sanitization

Layer / File(s) Summary
Sanitizer contract and coverage
core/providers/utils/utils.go, core/providers/utils/utils_test.go
Adds deterministic sanitization helpers using hashing and validates character safety, length limits, uniqueness, and pointer behavior.
Chat conversion identifiers
core/providers/anthropic/chat.go
Sanitizes tool-use and tool-result IDs during request construction, response conversion, and streaming event emission.
Responses streaming identifiers
core/providers/anthropic/responses.go
Sanitizes IDs for streamed computer, web search, advisor, code execution, generic, and MCP tool blocks.
Responses block conversion identifiers
core/providers/anthropic/responses.go
Sanitizes tool-use, tool-result, server-tool, MCP, web-fetch, and caller tool identifiers across conversion helpers.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: danpiths, TejasGhatte, akshaydeo

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed It clearly describes the Anthropic tool_use/tool_result ID sanitization fix.
Description check ✅ Passed The PR description covers the summary, changes, type, affected areas, testing, breaking changes, and security sections.
Linked Issues check ✅ Passed The changes address #5097 by sanitizing Anthropic tool IDs across Chat Completions and Responses, including streaming and non-streaming paths.
Out of Scope Changes check ✅ Passed The diff appears scoped to Anthropic ID sanitization and its tests, with no unrelated code changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@akshaydeo
akshaydeo merged commit 095c69d into maximhq:dev Jul 12, 2026
5 checks passed
akshaydeo pushed a commit that referenced this pull request Jul 14, 2026
…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.
akhsaul pushed a commit to akhsaul/bifrost that referenced this pull request Aug 27, 2026
…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.
occcat pushed a commit to occcat/bifrost that referenced this pull request Sep 2, 2026
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Anthropic rejects replayed tool_use/tool_result ids from non-conforming upstream providers

2 participants