-
Notifications
You must be signed in to change notification settings - Fork 582
ServerToolResult not delivered when client tools interrupt the stream (code_execution + client tool concurrency) #1325
Description
Bug Report: ServerToolResult not delivered when client tools interrupt the stream
Summary
When the Messages API returns stop_reason=tool_use (requesting execution of a client-defined tool) while server-side tools (code_execution, web_search) are running concurrently in the same turn, the ServerToolResult content blocks for those server tools are never delivered to the caller — neither during streaming, in the final message, nor in the subsequent API call.
Reproduction
Setup
- Model:
claude-sonnet-4-6(also observed onclaude-opus-4-6) - Tools:
code_execution_20250825(server-side) + a client-defined tool (e.g.ask_internal_tool) - Streaming mode via Python SDK
client.messages.stream()
Steps
- Send a message that triggers Claude to invoke both a server tool and a client tool in the same turn
- Example prompt: "Search our database for port activity data and create an Excel spreadsheet with a benchmarking report" (where the database search is a client-side tool)
What happens
- Stream begins
content_block_startwithserver_tool_use(e.g.text_editor_code_execution, id=srvtoolu_A)content_block_startwithserver_tool_use(e.g.web_search, id=srvtoolu_B)content_block_startwithtool_use(client tool, id=toolu_C)message_deltawithstop_reason=tool_usemessage_stop
Missing: No content_block_start with text_editor_code_execution_tool_result for srvtoolu_A, no web_search_tool_result for srvtoolu_B. The stream ends before these arrive.
What happens in the next API call
- We send the client tool result back in the conversation
- Claude continues and produces the file successfully using new tool calls
- The original
srvtoolu_Aandsrvtoolu_Bresults are never delivered as content blocks — neither in the streaming events nor inget_final_message()
Expected behavior
Either:
- (a) The stream should wait for all pending server tools to complete before returning
stop_reason=tool_use, OR - (b) The
ServerToolResultblocks should be included in the response of the next API call (since the results were computed and are available in the container), OR - (c) The
get_final_message()from the first call should include theServerToolResultblocks for tools that completed while the stream was open
Impact
Any application that uses server-side tools (code_execution, web_search) alongside client-defined tools will have orphaned server_tool_use blocks with no matching result. This causes:
- Frontend stuck spinners — tool progress UI shows a tool as "running" forever
- Incorrect error detection — monitoring/load tests flag these as failures even though the operation succeeded
- Workaround burden — applications must implement heuristic cleanup (assume completion at next iteration) rather than relying on the API contract
Evidence
From our production load test (5 concurrent users, file-generation questions):
| Test | Orphaned tools | File produced? | stop_reason |
|---|---|---|---|
| user_2 q1 | text_editor_code_execution |
Yes | end_turn |
| user_3 q1 | text_editor_code_execution |
Yes | end_turn |
| user_4 q1 | text_editor_code_execution + web_search |
Yes | end_turn |
| user_5 q1 | text_editor_code_execution + web_search |
Yes | end_turn |
All cases: server tool started at t=4-6s, client tool started at t=7-14s, stream ended for client tool. Server tool result never delivered. File was eventually produced in subsequent iterations.
Raw event trace (user_4, showing both orphaned types)
t=4.1s content_block_start server_tool_use text_editor_code_execution (srvtoolu_A)
t=4.7s content_block_start server_tool_use web_search (srvtoolu_B)
t=7.9s content_block_start tool_use ask_internal_tool (toolu_C)
... no content_block_start for *_tool_result for srvtoolu_A or srvtoolu_B ...
message_delta stop_reason=tool_use
message_stop
--- Next API call (with toolu_C result) ---
t=319s content_block_start server_tool_use text_editor_code_execution (srvtoolu_D) <- NEW tool
... srvtoolu_A and srvtoolu_B results never appear ...
Environment
- anthropic Python SDK version: 0.52.0
- Model: claude-sonnet-4-6
- Tools:
code_execution_20250825+ custom client tools - Streaming: yes
- Container: reused across iterations via
container.id
Workaround
We emit synthetic tool_result events for orphaned server tools at the start of the next agentic loop iteration, assuming they completed successfully (since Anthropic continues the conversation without error).