← Back to Blog

Fix: tool_result / tool_use mismatch — Claude Code 400 errors through a proxy

2026-09-18·4 min read·CodeRouter Team
tool_result tool_use mismatchclaude code 400 tool usemultiple tool_result blocksvalue did not match any expected varianttool_use_id not foundclaude code proxy tool call error

TL;DR — Anthropic's tool-call contract is strict: every tool_use block needs exactly one matching tool_result with the same id, in the next user turn. Break the pairing and you get a 400 on the step after the tool call, never on the call itself. Through a proxy this is almost always the translation layer — converting to another provider's tool format and back loses ids, duplicates results, or reorders blocks. Find the failing turn by logging the outbound body, not the error.

The errors

Several messages, one underlying cause:

400 — multiple tool_result blocks for a single tool_use id
400 — Invalid 'input': value did not match any expected variant
400 — tool_use ids were found without tool_result blocks immediately after

All share the same signature: the first tool call succeeds, the next request fails. Nothing in your code changed between them — the conversation state did. That is the same shape as the DeepSeek reasoning_content 400, and for the same underlying reason.

The contract

Anthropic requires, on the turn after a tool call:

Strict, and deliberately so: the model has to be able to reconstruct what happened. Which is also why a lossy round-trip through another format breaks it.

Why proxies break it

Converting Anthropic tool calls into OpenAI's tool_calls shape and back is not lossless:

Finding it

The error names a symptom, not a turn. Log the outbound request body on failure and check, in order:

  1. Count tool_use ids in the assistant turn, count tool_result ids in the next user turn — they must match exactly, no extras, no misses.
  2. Compare the id strings, not the counts. Regenerated ids give you equal counts and zero matches.
  3. Confirm results sit in the immediately following message.
  4. Look for two results carrying the same id — the parallel-call flattening case.
uses = {b["id"] for b in assistant["content"] if b.get("type") == "tool_use"}
results = [b["tool_use_id"] for b in following["content"] if b.get("type") == "tool_result"]
assert uses == set(results), f"orphans: {uses ^ set(results)}"
assert len(results) == len(set(results)), "duplicate tool_result ids"

Run that assertion in your proxy before forwarding and the bug reports itself at the point of corruption instead of as a 400 two layers away.

Fixes

Prevention

Part of the LLM API Error Reference — errors indexed by their exact strings.

Ready to Reduce Your AI API Costs?

CodeRouter routes every API call to the optimal model — automatically. Start saving today.

Get Started Free →

Get weekly AI cost optimization tips

Join 2,000+ developers saving on LLM costs