[Bug]: Claude Code tool search: first request of every session rejected with 400 (tool_addition content blocks not accepted by /v1/messages)
Correction (2026-09-17): the diagnosis below is wrong. The 400 is not caused by
AnthropicTool.input_schema. Every tool in the real request carries aninput_schema; the request is rejected becausemessages[]containstool_additioncontent blocks, whichAnthropicContentBlock.typedoes not accept. Captured request, error body and checks are in this comment. The symptom described below is accurate; the cause, the ~163-tool extrapolation and the curl reproduction are not. Original text kept unchanged for the record.
Your current environment
vLLM: 0.1.dev20073+g8e685d198
Client: Claude Code 2.1.273 (macOS), ANTHROPIC_BASE_URL pointed at this endpoint
Model: Qwen3.8-Flash-Next (NVFP4)
Serving: --max-model-len 262144 --enable-prefix-caching --enable-chunked-prefill
Hardware: NVIDIA DGX Spark (GB10), 128 GB unified memoryDescribe the bug
Same validation point as #46790, different trigger, and a different practical impact.
#46790 reaches AnthropicTool.input_schema through Anthropic server tools (web_search_20250305, computer use, bash, text_editor, code_execution), which carry no schema by design. The same required field also rejects deferred tools. With ENABLE_TOOL_SEARCH=true, Claude Code omits most tool schemas from the first request of a session and sends names only, so every one of those entries fails the same check and a single request produces a multi-error response.
Unlike the WebSearch case, this one is silent. The proxy in front of vLLM logs the rejection:
[WARN] non-200 response, recording partial metrics: status=400, path=/v1/messages
[INFO] Request ... "POST /v1/messages HTTP/1.1" 400 20214 "claude-cli/2.1.273 (external, sdk-cli)" 19.705389msand vLLM logs the retry landing on the same connection:
INFO: ... - "POST /v1/messages?beta=true HTTP/1.1" 400 Bad Request
INFO: ... - "POST /v1/messages?beta=true HTTP/1.1" 200 OKThe client swallows the 400 and resends with full schemas. The session completes normally and nothing appears on the command line, so the cost is easy to miss: one rejected round-trip plus a ~20 KB error body on every session start.
Over one afternoon on this setup: 29 × 200 and 3 × 400. Each 400 landed on the first request of one of the three sessions that announced deferred tools, matching to the second; the four sessions without deferred tools produced none. All three rejected bodies were 20214 bytes.
I did not capture the request body, so the entry count is inferred. Sending N name-only tools to the same endpoint grows the error body linearly at roughly 123 bytes each:
name-only tools response body
1 234
10 1343
30 3823
60 7543
100 12504Extrapolating 20214 bytes gives ~163 entries. The client's session transcript recorded total_deferred_tools: 148, the same order of magnitude.
Minimal reproduction, no client needed:
curl -s -X POST "$BASE_URL/v1/messages?beta=true" \
-H 'content-type: application/json' \
-H 'anthropic-version: 2023-06-01' \
-d '{"model":"<model>","max_tokens":8,
"messages":[{"role":"user","content":"hi"}],
"tools":[{"name":"Tool000"},{"name":"Tool001"}]}'{"error":{"message":"2 validation errors:\n {'type': 'missing', 'loc': ('body', 'tools', 0, 'input_schema'), 'msg': 'Field required', 'input': {'name': 'Tool000'}}\n ...","type":"Bad Request","param":"body.tools.0.input_schema","code":400}}The deferral flag is already plumbed through. _convert_tools in vllm/entrypoints/anthropic/serving.py forwards tool.defer_loading into the converted tool definition (line 581), directly after "parameters": tool.input_schema (line 579). Deferral is not an unknown concept on this path; there is a slot prepared for it. A deferred tool has no schema by definition, and AnthropicTool.input_schema in vllm/entrypoints/anthropic/protocol.py:79 is required, so the request is rejected at validation and never reaches that slot.
Verified unchanged on main (528fa835fd37, 2026-09-17) and in the current release v0.29.0 (2026-09-09); input_schema sits at line 79 and defer_loading at line 81 in both.
One note on #46790's conclusion that "such a 400 doesn't seem to have any practical impact on usage": that holds for server tools, where the effect is confined to WebSearch being unavailable. On this path it is a per-session tax that nobody sees, on a setup where the endpoint is otherwise working correctly.
#46797 makes input_schema optional and skips schema-less entries during conversion, which would unblock this path as well as the server-tool one it was written for. It was rebased on 2026-09-01 and has had no review since it was opened.
Before submitting a new issue...
- Make sure you already searched for relevant issues, and asked the chatbot living at the bottom right corner of the documentation page, which can answer lots of frequently asked questions.
Source: vllm-project/vllm