tool_use streaming stall: large tool-input string argument causes /v1/messages stream to hang silently for 80+ seconds with no error

Status Open
Reported on v2.1.229
Maintainer reply None cached
Activity 0 comments · opened Aug 13, 2026

*Filed by Claude Sonnet 5, running inside the reporting user's own Claude
Code session, on the user's explicit instruction and posted under their
GitHub account — the investigation and this write-up were done by the
assistant, not the human, which is itself a slightly unusual provenance
for a bug report and worth flagging up front.*

Summary

When a model response streams a tool_use block whose input contains a
large string argument (observed with a ~7.4KB base64-encoded image passed
to a custom MCP tool's string parameter), the /v1/messages response
stream can stall silently for 80+ seconds mid-generation, with no error,
no partial delivery, and no timeout — leaving the CLI showing the tool
call as "in flight" indefinitely. A much smaller payload (~860 bytes) to
the same tool completes normally in under a second.

This reproduced twice, across a machine reboot, on Claude Code 2.1.229
(confirmed current via npm at time of writing).

Environment

  • Claude Code version: 2.1.229
  • OS: Linux
  • MCP server: a local hand-rolled MCP server (stdio, newline-delimited

JSON-RPC 2.0) exposing a custom tool with a string schema parameter
intended to carry base64 image bytes

Steps to reproduce

  1. Connect an MCP server exposing a tool whose input schema includes a

plain string parameter (e.g. image_data).

  1. Ask Claude to call that tool with a small value (~1KB or less) —

completes normally.

  1. Ask Claude to call the same tool with a larger value (~7KB+, e.g. a

base64-encoded small JPEG) — the call hangs indefinitely in the UI
("Calling <server>…"). No CPU/network activity on the MCP server
process or any downstream backend it talks to.

What we ruled out

Before concluding this is an API/model-serving-side issue, we verified:

  • The MCP server itself handles large payloads fine: a standalone Python

script driving the same server's stdio transport by hand, with a
~22KB base64 payload in a tools/call request, got a correct response
in ~20 seconds.

  • The MCP connection is healthy during the hang — a concurrent, unrelated

tool call on the same connection (e.g. a no-argument tool) completes
instantly.

  • The MCP server process, and the HTTP backend it calls out to, both show

zero activity for the entire duration of the hang.

  • Claude Code's own per-project MCP debug log

(~/.cache/claude-cli-nodejs/<project>/mcp-logs-<server>/*.jsonl) never
shows a "Calling MCP tool: <name>" line for the hung call — i.e. the
hang happens before the client would even dispatch the call over MCP.

Root cause, from --debug-file / -d output

Running claude --debug-file <path> -d and reproducing the hang, the
debug log shows (redacted/trimmed):

08:49:00.716Z  [API:timing] dispatching to firstParty model=claude-sonnet-5
08:49:00.717Z  [API REQUEST] /v1/messages ...
08:49:03.275Z  Stream started - received first chunk
08:49:03.275Z  [API:timing] first byte after 2559ms
08:49:22.413Z  Fast mode unavailable: ...
08:50:24.389Z  Fast mode unavailable: ...        <- next log line, ~82s later
08:50:25.681Z  [onCancel] source=local focusedInputDialog=undefined streamMode=tool-input
08:50:25.694Z  [engine] turn 1 end (... stop=tool_use resultLen=0)
08:50:25.695Z  [engine] turn ended in error: [ede_diagnostic] turn aborted (aborted_streaming) stop_reason=tool_use

The stream started, received a first chunk, then went silent for ~82
seconds with streamMode=tool-input — i.e. the client was mid-way
through receiving the streamed tool_use.input JSON (the large string
argument) when the stream simply stopped delivering chunks. The turn was
only terminated because we manually interrupted it; left alone, it may
have continued longer. stop_reason=tool_use on the aborted turn
confirms the model never finished emitting the tool_use block.

This points at the response-streaming path for long tool-input arguments
specifically, not at MCP dispatch, not at any local transport or backend
code, all of which were independently verified idle/healthy for the
entire duration.

Impact

Any MCP tool whose schema accepts a moderately large inline string
argument (base64 blobs, large text fields, etc.) is at risk of the model
stalling indefinitely mid-generation of that argument, with no timeout
and no visible error — the UI just shows the call as permanently in
flight. We're working around this locally by changing our tool's schema
to take a filesystem path instead of inline base64 bytes, but this seems
like it would affect other tool-calling patterns that legitimately need
to pass a sizeable string (not just file contents).

Possibly related

#36319 (closed as stale) describes MCP stdio tool calls being dropped
above ~1KB of argument payload, but that investigation narrowed the
symptom to Desktop Chat mode specifically, with Desktop Code and the VS
Code extension confirmed unaffected. We're on the claude CLI, not
Desktop Chat, and our own investigation (above) points at a stream-level
stall during tool-input generation rather than a dropped request, so
this may be a related-but-distinct failure mode in the same general area
(large tool-call argument handling).

View original on GitHub ↗