Tool dispatch stalls silently in 2.1.121–2.1.123: tool_use emitted, no tool_result, no disk side-effects, no errors
Tool dispatch stalls silently in 2.1.121–2.1.123: tool_use emitted, no tool_result, no disk side-effects, no errors
Summary
In claude CLI versions 2.1.121, 2.1.122, and 2.1.123 (Claude Code), local tool calls (Write, Bash, Edit, Read) intermittently stall after the model emits a tool_use block. The tool handler is never invoked: no disk I/O occurs, no tool_result is emitted, no permission prompt fires, and no error is logged on the CLI side. The stall persists indefinitely from the CLI's perspective; only an external watchdog can recover the subprocess.
This appears to be a regression. We have not observed the stall on 2.1.120 or earlier in the same workload.
Environment
- OS: macOS 25.4.0 (Darwin)
- Node: (default; can supply on request)
- Install path:
/opt/homebrew/bin/claude→@anthropic-ai/claude-code - Versions where observed: 2.1.121, 2.1.122, 2.1.123
- Last known good: 2.1.120
- Invocation:
claude --print --permission-prompt-tool stdio(subprocess invoked from a parent harness that responds to permission prompts on the CLI's stdio channel)
Signature (how to detect the bug from logs)
Three coincident facts in a single CLI subprocess turn:
- The session jsonl at
~/.claude/projects/<project>/<sessionId>.jsonlcontains an assistant message with atool_useblock ({"type":"tool_use","id":"toolu_…","name":"Write",…}). - No matching
tool_resultentry for thattool_use_idappears anywhere later in the same jsonl. - The intended side-effect of the tool did not happen — e.g. for
Write, the targetfile_pathdoes not exist on disk (or exists only with content from an unrelated later attempt).
There is no accompanying error on stderr, no permission-prompt-tool error, and no Tool permission stream closed before response received message. The CLI gives no observable signal that anything has gone wrong; from the assistant's perspective the call is simply pending forever.
Empirical incidence
In a single host running real interactive workloads:
| Date | Distinct stalled tool-uses |
|------|----------------------------|
| 2026-04-26 (CLI 2.1.120) | 0 (different bug class observed — see "Distinguishing from prior bug" below) |
| 2026-04-27 (CLI 2.1.120 → 2.1.121) | 0 |
| 2026-04-28 (CLI 2.1.121 → 2.1.122 → 2.1.123) | 1 |
| 2026-04-29 (CLI 2.1.123) | 11 |
Tool distribution across the 12 events: Write ×6, Bash ×3, Edit ×2, Read ×1. No correlation with input size or content type — Read ×1 was a small local file; Write ×6 ranged from short text to multi-KB content. No correlation with concurrent MCP load that we can see.
Reproducer status
We have not been able to reproduce the stall with a minimal claude --print "Write hello to /tmp/x"-style invocation. Out of two minimal-repro attempts on 2026-04-29, both completed cleanly in ~9s. The stall appears to require some combination of: a real model turn with prior context, MCP servers loaded, multiple back-to-back tool calls, or model latency above some threshold. We have not isolated the trigger.
If your team can reproduce with a constructed prompt that emits multiple tool calls under MCP load, that would likely be faster than my isolation effort. Happy to provide:
- Sanitized session jsonl excerpts containing the orphan
tool_useblocks and the surrounding turn structure - Full gateway-side log of all 12 stalled events with timestamps and tool IDs
- Our MCP server config (raven, linear, openclaw, framer, outseta)
Distinguishing from the prior perm-stream bug
We previously observed a different hang class on 2.1.120 where the --permission-prompt-tool stdio stream broke and subsequent tool calls returned is_error: true with body Tool permission stream closed before response received. That class appears patched on 2.1.121+ — zero occurrences of that error string in any session jsonl or gateway log since 2026-04-27. The current dispatch stall is silent (no error string, no perm-stream traffic at all), affects the same tools, and has comparable wall-clock impact.
We're not claiming causation, but the timing is suggestive: the perm-stream class disappears in 2.1.121 and the dispatch-stall class appears the same week. May or may not be related changes.
Workaround in place
External watchdog: parent process tracks tool_use start times and aborts the CLI subprocess when any tool exceeds a configurable max age (default 5 min, currently tuned to 10 min general / 240 s for "fast" tools like Read). On abort, the next user message starts a fresh CLI subprocess and recovers cleanly.
This bounds the loss but doesn't fix the bug. Each incident still costs:
- 1 full conversational turn discarded
- ~5 min wall-clock wait
- Whatever the tool was supposed to do (writes lost, edits lost)
- Partial-output orphan in the assistant stream
At 11 incidents on 2026-04-29 alone, that's ~55 min/day of wasted runtime and ~11 user-visible dropped tool calls per day on this one host.
Asks
- Triage: confirm whether anyone else is seeing this on 2.1.121+ and whether you have an internal repro.
- If actionable: point me at a code path or a diagnostic flag I can flip to surface the stall on the CLI side. I'd much rather catch it inside the CLI than from a parent watchdog.
- If a fix lands: I'll re-run the same watchdog-instrumented workload and report incidence at the new version. Trivial to verify.
Happy to provide additional artifacts. Thanks for the work on Claude Code — it has been excellent overall, this is a noisy edge.
---
Reporter context: this issue is filed by a power user running Claude Code as a subprocess of a custom harness. All tool_use IDs cited above are from real internal sessions; full logs available on request via private channel if helpful.
This issue has 12 comments on GitHub. Read the full discussion on GitHub ↗