[BUG] Claude Code CLI hangs indefinitely after a successful tool_use response is lost in streaming receive
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Claude Code CLI hangs indefinitely after a successful tool_use response is lost in streaming receive
Closest existing issues: #46767 ("missing tool result" regression). Also related: #47996, #50727, #50185, #44783, #52544. None appear fully fixed — symptoms keep resurfacing.
Summary
When Claude Code runs in a tmux pane and executes a tool call that completes successfully server-side, there is a window in which the CLI can lose the streamed tool_result and the UI remains in the Running… / Undulating… (Ns · ↓ N tokens) state forever. The HTTP connections stay healthy and idle (no kernel-side queued data), the main thread enters a low-CPU livelock (wakes ~100×/sec in epoll_wait with no forward progress), and no transcript events are written. Pressing ESC in the terminal cleanly cancels the in-flight receive; the CLI then re-syncs and reports the operation had already succeeded.
Actual
- UI stuck on
Running…indefinitely. Receive indicator shows a fixed byte count (e.g.↓ 23.5k tokens) for hours with no progression. - Queue events (
queue-operation/enqueue) accumulate as the user types into the stuck prompt, but are never dequeued. - No transcript events are written for the duration of the hang.
ESCcancels the stuck receive; the CLI then discovers the tool had already succeeded and continues normally.
Forensic evidence
Captured while hung (before sending ESC):
| Source | Shows |
|---|---|
| /proc/<pid>/status, thread stacks | Main thread in do_epoll_wait; 11 threads; process alive |
| lsof + ss -tnpo | 4 ESTABLISHED TLS connections to the API endpoint with zero Recv-Q / Send-Q — kernel sees no buffered data |
| /proc polling (6 samples, 2s apart) | ~8% main-thread CPU, ~10% total over 20s. Normal idle CLI is < 1% |
| Process tree | tmux pane still attached; pty intact; not a detached-pty scenario |
| Transcript .jsonl | Last event before hang is the tool_use at 19:31:55; no further writes for 2h13m |
Empty Recv-Q / Send-Q on all four connections is the key data point: there is no buffered data for the CLI to read. Either the server's response was already fully consumed at the kernel-user-space boundary (and something in the CLI failed to process it), or it never arrived and no timeout fires.
Happy to share full sanitized captures (proc-state, thread-stacks, fd-and-sockets, CPU-poll samples) if useful — drop a note here.
Hypotheses
- Race in SSE / streaming-response parser. A final event (likely the
message_stopor equivalent) arrives concurrently with an internal state transition and is dropped; the fetch promise neither resolves nor rejects. - Timer drift in keepalive / watchdog. The CLI's own watchdog for stuck requests may be reset by the process's hot-polling loop and never fires.
- Interaction with tmux
SIGWINCHor raw-mode toggle. Two/dev/pts/*fds were opened around the time the hang set in (prior to the triggering tool call), suggesting a raw-mode / signal-handler pipe manipulation. A dropped signal could leave an event-loop handler dangling.
Suggested mitigations
- Hard deadline on streaming receives. If no bytes arrive for N seconds on an in-flight tool_result receive, abort with an error the UI can surface and optionally auto-retry.
- Dispatch-layer healthcheck. The main event loop waking ~100×/sec with no progress is a diagnosable signature; a lightweight heartbeat comparing expected-advancing state vs. observed could detect the livelock and emit diagnostics.
- Persist enough state that an in-flight receive can resume after an ESC. Today ESC simply drops the receive; the CLI then relies on re-sending the context. For long tool results this is wasteful. A checkpoint of last-seen server-side event id would let the client resume.
Notes
- No destructive action was required —
tmux send-keys -t <pane> Escapecleaned up the state fully. - A user-side watchdog now auto-detects and auto-recovers this specific pattern (sends
Escapeviatmux send-keys). That is a workaround, not a fix. - After the recent CLI upgrade this hang has become very common in our environment — multiple times per hour during active use. Filing now because the watchdog can mask the symptom but not the root cause.
What Should Happen?
- After a successful tool invocation, the tool_result is delivered and the conversation continues.
- If the streaming receive fails, the CLI should surface an error or auto-retry, not hang silently.
Error Messages/Logs
Not deterministic on demand. Organic reproduction captured 2026-04-24 on:
- CLI version: `2.1.118`
- Platform: Linux (LXC container, Proxmox host, Debian 12)
- Terminal: `tmux` 3.3a
- Session start: 2026-04-23 16:09 CEST
- Hang start: 2026-04-24 19:31:55 UTC (`toolu_01KkzPcqKkt2FBQ6Mt7jHg8S`)
- Hang duration: 2 h 13 m 53 s until `ESC` recovered it at 21:45:48 UTC
The triggering tool call was a compound Bash command of the shape:
git stash push -m "pre-pull stash" <file-a> <file-b> && git pull --rebase && git stash pop
The command succeeded on disk (confirmed via `/status` after recovery: *"Git state: clean rebase (stash/pop completed successfully)"*). The hang was on the client's side of the receive stream.
Steps to Reproduce
- Run Claude in a detatched tmux on ubuntu 24
- Start /remote-control
- Detatch the terminal
- Remotely manage via mobile app or desktop app.
- Use the session activly for a longer period
Claude Model
Opus
Is this a regression?
No, this never worked
Last Working Version
_No response_
Claude Code Version
2.1.119
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Other
Additional Information
I'm suspecting the hit-rate is higher when auto-mode is not used, but I can't prove that,. Sometimes it works well for 24h, sometimes just a few minutes.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗