claude-code - 💡(How to fix) Fix [BUG] CLI hangs indefinitely on stuck streaming response — UI swallows input but redraws on SIGWINCH; only external kill recovers

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

A streaming request that stalls (no bytes received for some inactivity window) should be aborted automatically, the user should be shown an error like "stream stalled, retry?", and the TUI should return to an interactive prompt — without requiring the user to kill the process from another terminal.

Error Messages/Logs

Root Cause

  • The event loop is healthy — SIGWINCH triggers a full Ink re-render.
  • stdin (raw mode, isig disabled) is still being read, but the keystroke handler is awaiting a promise that never resolves — that promise is the streaming response.
  • The TCP looks ESTABLISHED locally because the kernel never received FIN/RST (a proxy/NAT dropping the upstream after sleep/wake or a peer crash without an RST will produce exactly this state).
  • There is no client-side inactivity / read timeout on the response stream, so the for await (chunk of stream) loop never gives up.
  • Because raw mode disables isig, keyboard Ctrl+C is delivered as byte 0x03 (not SIGINT), so it goes through the same wedged input pipeline. Only an external kill -INT reaches the process.

Fix Action

Fix / Workaround

sample(1) — main thread parked on kevent64, "HTTP Client" Bun thread also on kevent64:

2488 Thread_xxx DispatchQueue_1: com.apple.main-thread (serial) ... kevent64 (in libsystem_kernel.dylib) + 8 [0x18d9cbba8] 2488 Thread_xxx: HTTP Client ... kevent64 (in libsystem_kernel.dylib) + 8 [0x18d9cbba8]

No CPU, no work pending — event loop is alive but every thread is idle waiting on I/O.

Code Example

# Hung process — alive, idle, single dangling socket:
$ ps -p 3771 -o pid,state,etime,%cpu,wchan,command
  PID STAT ELAPSED %CPU WCHAN COMMAND
 3771 S+   45:45    1.0 -     claude --resume

$ lsof -p 3771 -nP | grep TCP
claude 3771 ... TCP 198.18.0.1:54969 -> 198.18.0.28:443 (ESTABLISHED)
# (Healthy claude processes on the same machine have 35 rotating sockets;
#  the hung one has exactly one, on the dead stream.)

# sample(1) — main thread parked on kevent64, "HTTP Client" Bun thread also on kevent64:
2488 Thread_xxx   DispatchQueue_1: com.apple.main-thread  (serial)
  ... kevent64  (in libsystem_kernel.dylib) + 8  [0x18d9cbba8]
2488 Thread_xxx: HTTP Client
  ... kevent64  (in libsystem_kernel.dylib) + 8  [0x18d9cbba8]
# No CPU, no work pending — event loop is alive but every thread is idle waiting on I/O.
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • I am using the latest version of Claude Code

What's Wrong?

The CLI enters a permanent "soft-hang" state mid-conversation:

  • The in-flight streaming response to the Anthropic API never completes.
  • Keyboard input (including Ctrl+C and Esc) is silently consumed — no echo, no visible effect.
  • The TUI still redraws on window resize (SIGWINCH), so the process is clearly not crashed.
  • The only way out is kill -INT/-TERM/-9 from a separate terminal. claude --resume after that recovers the session intact.

I've hit this many times in the last few weeks. It reproduces reliably after the laptop sleeps/wakes or after a local network/proxy reconnects while a streaming response is in flight. No client-side inactivity timeout appears to fire — I've left a wedged session sitting for 45+ minutes with zero recovery.

What Should Happen?

What Should Happen? (required)

A streaming request that stalls (no bytes received for some inactivity window) should be aborted automatically, the user should be shown an error like "stream stalled, retry?", and the TUI should return to an interactive prompt — without requiring the user to kill the process from another terminal.

At minimum, Ctrl+C (or Esc) should reliably cancel an in-flight request even when the response stream is wedged.

Error Messages/Logs

# Hung process — alive, idle, single dangling socket:
$ ps -p 3771 -o pid,state,etime,%cpu,wchan,command
  PID STAT ELAPSED %CPU WCHAN COMMAND
 3771 S+   45:45    1.0 -     claude --resume

$ lsof -p 3771 -nP | grep TCP
claude 3771 ... TCP 198.18.0.1:54969 -> 198.18.0.28:443 (ESTABLISHED)
# (Healthy claude processes on the same machine have 3–5 rotating sockets;
#  the hung one has exactly one, on the dead stream.)

# sample(1) — main thread parked on kevent64, "HTTP Client" Bun thread also on kevent64:
2488 Thread_xxx   DispatchQueue_1: com.apple.main-thread  (serial)
  ... kevent64  (in libsystem_kernel.dylib) + 8  [0x18d9cbba8]
2488 Thread_xxx: HTTP Client
  ... kevent64  (in libsystem_kernel.dylib) + 8  [0x18d9cbba8]
# No CPU, no work pending — event loop is alive but every thread is idle waiting on I/O.

Steps to Reproduce

  1. Start a session in a terminal: claude --resume
  2. Send a prompt that yields a long streaming response.
  3. While the response is streaming, cause the underlying TCP connection to silently die. Reliable triggers in my setup:
    • Sleep/wake the laptop
    • Reconnect / switch nodes in a local VPN/proxy (fake-IP routing in my case, but I believe any network event that drops the TCP without sending FIN/RST will do — e.g. WiFi roam, NAT timeout)
  4. The TUI is now frozen:
    • Typing produces no echo and no UI reaction
    • Ctrl+C / Esc do nothing
    • Resizing the terminal window DOES trigger a clean repaint (SIGWINCH path works)
  5. No timeout fires. Observed > 45 minutes wedged.
  6. Recovery requires kill -INT <pid> (or -TERM/-9) from another terminal, then claude --resume.

Why this happens (best guess)

  • The event loop is healthy — SIGWINCH triggers a full Ink re-render.
  • stdin (raw mode, isig disabled) is still being read, but the keystroke handler is awaiting a promise that never resolves — that promise is the streaming response.
  • The TCP looks ESTABLISHED locally because the kernel never received FIN/RST (a proxy/NAT dropping the upstream after sleep/wake or a peer crash without an RST will produce exactly this state).
  • There is no client-side inactivity / read timeout on the response stream, so the for await (chunk of stream) loop never gives up.
  • Because raw mode disables isig, keyboard Ctrl+C is delivered as byte 0x03 (not SIGINT), so it goes through the same wedged input pipeline. Only an external kill -INT reaches the process.

Suggested fixes (in order of impact)

  1. Stream inactivity timeout on the API client: if no bytes received for N seconds (configurable, e.g. 90s), abort the request and surface a retry prompt. This alone eliminates the hang.
  2. Always-available cancel keybind: route Ctrl+C through a synchronous abort path, not through the awaiting state machine.
  3. TCP keepalive on outbound sockets (socket.setKeepAlive(true, 30000)) — bounds the half-open window to ~75s after probing starts.
  4. Heartbeat indicator while waiting on bytes (a moving dot every few seconds), so "waiting" vs "frozen" is distinguishable to the user.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

No response

Claude Code Version

2.1.132 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Other

Additional Information

Additional Information (optional)

  • macOS 26.4.1 (Darwin 25.4.0), Apple Silicon.
  • Terminal: Ghostty (not in the dropdown — selected "Other").
  • A local VPN/proxy is in use (fake-IP routing via 198.18.0.0/15), which I believe is the trigger but not the root cause: any TCP that dies without FIN/RST should produce the same symptom, since the client has no inactivity timeout to catch it.

Not duplicates of:

  • #59810 (general timeout detection feature) — related direction, but this is a concrete reproducible bug, not a feature ask.
  • #59827 (goal function loop) — different layer.
  • #59750 / #59899 / #59814 (Windows TUI unresponsive) — different platform/path.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix [BUG] CLI hangs indefinitely on stuck streaming response — UI swallows input but redraws on SIGWINCH; only external kill recovers