[BUG] Claude Code hangs indefinitely when API streaming connection stalls (no read timeout)
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 hangs permanently when the API streaming response stalls mid-delivery. The process stays alive (epoll_wait in kernel) but makes no progress. The UI shows a spinner ("Accomplishing…",
"Ruminating…", etc.) indefinitely. No error is surfaced. The session cannot recover — the only fix is kill -9.
Two distinct hang patterns observed:
Pattern 1 — Mid-turn stream freeze: The API response starts streaming (thinking block received, "thought for 3s" renders), then the stream silently stops. No more tokens arrive. The JSONL session
log shows no new entries after the last progress update. The process is stuck waiting for bytes that never come.
Pattern 2 — Tool result delivery stall: A Bash tool executes and completes (e.g., curl --max-time 5), but the tool result is never delivered back to the conversation state. The last tool_use ID in
the JSONL has no matching tool_result. The UI shows the spinner as if the tool is still running.
Both patterns correlate with background agent (Task with run_in_background: true) notifications arriving between or during turns.
What Should Happen?
- Claude Code should have a read timeout on the SSE/streaming HTTP response. If no data arrives within N seconds (e.g., 60–120s), it should abort the request and surface a retryable error to the
user.
- Tool result delivery should have a timeout. If a tool completes but the result isn't consumed within N seconds, the session should error rather than hang.
- The UI should detect "no progress for N seconds" and offer the user an escape (e.g., "Session appears stuck. Press Enter to retry or Esc to cancel").
Error Messages/Logs
Session 1: Mid-turn stream freeze (fff87be2)
JSONL timeline:
21:43:28 UTC Turn 1 ends (system/turn_duration logged)
21:43:28 Background agent notification injected as user message
→ Triggers Turn 2
21:44:44 Turn 2 ends (system/turn_duration logged, stop hooks run)
Background agent notification for a DIFFERENT agent arrives
→ Triggers Turn 3
API call starts, "thought for 3s" renders in UI
*** No more JSONL entries. Stream froze. ***
UI stuck on "Accomplishing… (thought for 3s)" for 10+ minutes
Process state during hang:
$ ps -p 843072 -o pid,state,wchan
PID S WCHAN
843072 S do_epoll_wait
$ kill -TERM 843072 # no effect
$ kill -KILL 843072 # required to terminate
Session 2: Tool result delivery stall (06d50a72)
JSONL shows:
Last tool_use ID: toolu_vrtx_01JqgVUPZLrMyudEQaAQhx1x (Bash: curl)
Last tool_result ID: toolu_vrtx_01LJscxroh9R7tsDQUL6AhuF (different, earlier tool)
→ Mismatch: the curl's result was never delivered
The curl command had --max-time 5 so it completed, but the result never made it back to the conversation. UI showed "Ruminating…" indefinitely.
Steps to Reproduce
Difficult to reproduce deterministically — it appears to be a race condition or network-level issue. But the following pattern triggers it frequently:
- Start Claude Code on a remote Linux server via SSH + tmux
- Give it a complex task that spawns multiple background agents (Task with run_in_background: true)
- Wait for background agents to complete and deliver notifications
- When a notification arrives right as a turn is ending or between turns, the next API call has a high chance of hanging
Environment factors that may contribute:
- Remote server (high-latency network path to API)
- tmux (terminal multiplexing)
- Multiple concurrent Claude Code sessions in different tmux panes
- Heavy hook infrastructure (6 hook events, though all complete in <120ms per telemetry)
Claude Model
Opus
Is this a regression?
Yes, this worked in a previous version
Last Working Version
2.1.40
Claude Code Version
2.1.42
Platform
Google Vertex AI
Operating System
macOS
Terminal/Shell
iTerm2
Additional Information
Suggested fix
Add a read timeout to the HTTP streaming client. Pseudocode:
// In the SSE/streaming response handler:
const STREAM_READ_TIMEOUT_MS = 120_000; // 2 minutes
let lastDataTime = Date.now();
stream.on('data', (chunk) => {
lastDataTime = Date.now();
// ... process chunk
});
const watchdog = setInterval(() => {
if (Date.now() - lastDataTime > STREAM_READ_TIMEOUT_MS) {
stream.destroy(new Error('API stream read timeout'));
clearInterval(watchdog);
}
}, 10_000);
Workaround
External watchdog daemon that monitors JSONL session files and kills processes with no writes for >5 minutes. Available at user's request.
34 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
I also experience the same. how to downgrade it?
Experiencing the same issue — all models affected, not just Opus 4.6
Environment:
claude.ai(first-party)Symptoms:
"are you there?") hangs indefinitely on spinner messages (shlepping,channeling,pondering…)What I've verified:
| Check | Result |
|-------|--------|
|
curl -s https://api.anthropic.com/v1/messages -o /dev/null -w "%{http_code}"|405— API is reachable ||
claude auth status| Logged in, valid credentials || Deleted
~/.claude/projects/and~/.claude/sessions/| No effect || Tested with Opus | Hangs |
| Tested with Sonnet | Hangs too |
| Tested via CLI | Hangs |
| Tested via Claude Desktop Code tab | Hangs |
Key observation:
This is not limited to Opus 4.6. Sonnet is equally affected, suggesting the streaming/SSE stall issue may be broader than initially reported.
I agree with you @fmallet I have the same problem since Monday, until yesterday I got it to kinda work by interrupting him and say "continue" but now it does not work at all.
Update: Resolved — corrupted
~/.claudedirectory was the root causeFollowing up on my previous comment. After extensive debugging, I found the fix.
What solved it
Removing the
~/.claudedirectory and letting Claude Code recreate it from scratch resolved the issue completely.What did NOT help
| Attempted fix | Result |
|---------------|--------|
| Deleting
~/.claude/projects/and~/.claude/sessions/| No effect || Updating Claude Code (2.1.45 → 2.1.62) | No effect |
| Launching from a clean directory (
/tmp/test-claude) | Still hung || Testing with Sonnet instead of Opus | Still hung |
Root cause hypothesis
The issue appears to be a corrupted state in
~/.claude/.claude.json, likely in thecachedGrowthBookFeatures(feature flags cache) or related cached data. Claude Code was failing during early initialization — before even reaching the API call or producing any debug output.Key observation
The hang affected all models (Opus and Sonnet), both CLI and Claude Desktop Code tab, but Chat and Cowork tabs in Claude Desktop worked fine (they don't go through the Claude Code client).
Suggestion for the Claude Code team
claude resetcommand would be very helpful for this type of situationHope this helps others experiencing the same issue.
@fmallet You are a legend, tried your solution and it worked. I tried to debug this the whole week and could not understand what is the problem.
it worked for a while then it stopped working again lol
Corroborating data from Bedrock user — 12-hour log analysis
I filed #29344 independently and was pointed here as a duplicate. My data corroborates everything in this issue, from the Bedrock side (vs your Vertex findings). Adding my analysis here since it has concrete numbers.
Environment
CLAUDE_CODE_USE_BEDROCK=1)us.anthropic.claude-opus-4-6-v1maxOutputTokens: 64000Aggregate Stats (12-hour window, evening through next afternoon)
| Metric | Value |
|--------|-------|
| Sessions active in period | 24 |
| Incomplete responses (
stop_reason: nullwith partial content) | 395 || Error entries logged for those failures | 0 |
| Stream stalls >= 3 minutes | 18 |
| Stream stalls >= 5 minutes | 14 |
| Max single stall duration | 599 seconds (~10 min) |
Zero errors were surfaced to the UI or logged to the session JSONL for 395 incomplete API responses.
Both patterns confirmed on Bedrock
Pattern 1 — Mid-turn stream freeze (same as OP's Pattern 1):
thinkingtoken → 552s stall → eventually resumed via system heartbeattool_use:Read→ 284s stall → system heartbeat broke itPattern 2 — Tool result delivery stall (same as OP's Pattern 2):
tool_resultdelivered → 592s wait → assistant finally respondedtool_resultdelivered → 577s wait → system heartbeattool_resultdelivered → 554s wait → system heartbeatThe 300-second heartbeat loop
Many sessions show repeating
system → systementries at exactly 300-second intervals, sometimes 10-15+ in a row. The worst case showed 25 consecutive 300s heartbeats spanning over 2 hours while the session appeared stuck to the user. The spinner runs the whole time with no indication anything is wrong.Impact
Key takeaway
This is not Vertex-specific — it reproduces identically on Bedrock. The core issue is that Claude Code has no client-side stream idle timeout and silently swallows incomplete responses without logging errors or surfacing them to the user.
FYI, I deleted my claude.json file, but that did not help.
ALSO FYI: This succeeded when run in OpenCode with the same model and thinking. So this is not a Bedrock issue.
This basically renders claude useless for me. Having to interrupt and type continue is only partially effective and it seems like it risks leaving things in an unknown state.
I've tried recreating all .claude directories and .json files, but no change. Happens for me in remote sessions and locally. In tmux and outside of tmux.
What additional information can be provided to accelerate this?
Using Version: 2.1.69 on a Mac locally and in Debian 12.10 remotely.
We reverse-engineered
cli.jsacross 12 npm versions and analyzed 1,571 sessions (148,444 tool calls, 8,007 orphaned) to find the root causes of this and related hang/orphan issues.Full analysis with code offsets and fix proposals: #33949
I'm still encountering this
i think it has something to do with reading a large file...
Here is the session with the agent, it says the agent is thinking, but there is no thinking event in the session.
and this entire time it is eating tokens. the entire time, it was reading this file for 22 minutes now
This occurs on both models opus and sonnet
a10aa665-717e-4272-87aa-0bdb5c8c1d52.txt
<img width="1417" height="223" alt="Image" src="https://github.com/user-attachments/assets/f2a66109-27e3-475b-95af-1be9ad5f37b5" />
splitting the file into multiple files, originally 1K lines into 5 files worked, but it still stalled for 5 minutes on first file.
This has been happening constantly this weekend.
Still reproducing on 2026-04-13 — 2+ minute Metamorphosing stall before the first server call
Adding a fresh data point since this is still happening on current Claude Code.
Environment
Symptom (from the top status line during a brainstorming prompt)
The session sat in
Metamorphosingfor 2 minutes 7 seconds with only 40 tokens downloaded and 109 s of reported "thought" before the first server call actually went out. Only 40 tokens landed during the entire 2+ minute window, and the spinner kept running the whole time. No error, no timeout, no indication to the user that anything was wrong — identical to the patterns described earlier in this thread.What the user actually experiences
Metamorphosing…immediately.Why I'm commenting here instead of a new issue
What would help end users right now, in rough order of impact:
Metamorphosinglabel collapses both states, which is why users cannot tell a slow generation from a dead stream.claude reset(or an in-session/reset) that nukes~/.claude/.claude.jsoncache so users don't have tomv ~/.claude ~/.claude.bakby hand.Happy to pull session logs if a specific format helps the triage team.
+1
The same problem occurred several times.
Same issue
Same issue and 73% of my weekly token wasted, thanks...
I keep getting "API Error: Stream idle timeout - partial response received" in Claude Code on the web. I think it might be related to CC processing large files? Anyway, when I run the same prompt in Claude Code CLI it works fine. Hopefully Anthropic can figure it out and fix Claude Code on the web, because it's so much easier to work there.
I fixed it by first running /compact in one command, then asking “please complete the code” in the next command—and it worked.
@mrayhanulmasud, cool, thanks. I might try that out if it keeps happening. Another possible workaround is to just log out of Claude and log back in.
The other thing that actually worked perfectly was just using CC CLI teleport, so you can continue a web based Claude Code session in the CLI, and surprisingly enough, it works!
Just run
claude --teleportin your terminal and open the CC web session.+1 — from the Remote Control side. Framed by Claude.
Scenario: session hangs mid-tool-call on the host (Windows 11, Opus 4.7).
Wall clock advances, token counter frozen. I'm away from the host, connected
via mobile Remote Control. The only recovery path is physically walking back
to the host to press ESC — which defeats the point of Remote Control.
Adding a concrete UX ask on top of the programmatic interrupt already
requested here:
### Remote Control "Stop" button
A visible cancel control in the Remote Control web & mobile clients,
mirroring the local ESC UX — active while a turn is in progress.
once the underlying issue is fixed: SIGINT-equivalent, cancel token,
whichever).
(#44921), not only during healthy tool calls — otherwise it won't help
the actual hang scenarios.
queues it behind the hung turn. A Stop button would clear the queue
or at least gate sending on successful interrupt.
### Why UI matters alongside the API
The programmatic interrupt asked for above covers SDK/CI users. The mobile
user isn't going to shell in from a phone to send a signal — they need a
button. Shipping only the API leaves Remote Control users without a
recovery path.
Related:
mechanism itself needs work)
Remote UI already has a Send/Stop state machine to build on)
Found a fix for the
API Error: Stream idle timeout - partial response receivederror. Adding one line to a config file resolves it for me. Full details below.The fix
Open
~/.claude/settings.jsonand add this to the"env"section (create the section if it doesn't exist):Then quit and restart Claude Code. That number is 30 minutes in milliseconds, replacing a hidden 90-second default that the client uses before assuming the connection has gone idle.
When I was hitting it
What didn't work
remoteControlAtStartup: false) — same failure on local CLIHow I tracked it down
The timing. Opened the session log and measured every timeout. They all fired between 90.0 and 91.7 seconds after the previous message — eight in a row. That's not random — it's a 90-second clock running out.
The binary. The
claude.exebinary referencesCLAUDE_STREAM_IDLE_TIMEOUT_MSin two places. The one my stream hits defaults to 90 seconds with no minimum, so raising the env var directly raises the ceiling. The variable is real and load-bearing — it's just not in the official env-vars reference, so nobody knew it was the lever.Confirmation it actually works
After setting the env var to 1,800,000 and restarting:
Suggestion for Anthropic
This variable is real and important but undocumented — please either add it to the env-vars reference page or raise the default. Opus 4.7 with thinking + large context easily goes silent for over 90 seconds while preparing a big tool call, and most users have no way to know what's wrong or how to fix it.
Cross-ref: #25979 (main tracking issue), #33949 (technical deep-dive).
See #54434 for a fresh repro with detailed mid-stream stall timing data; I added an OpenClaw-side independent confirmation here: https://github.com/anthropics/claude-code/issues/54434#issuecomment-4339679618
Same root cause as the 'no read timeout' pattern (your Pattern 1 — mid-turn stream freeze) documented in this issue, observed in a different invocation path (claude-cli spawned as a subprocess with
-p --output-format stream-json --verbose). Patch suggested in #25979's "What Should Happen" #1 (per-chunk read timeout on SSE) is exactly the right shape.Same issue when running in a remote visual studio code and i close my laptop. Historically it would have continued in the background.
Still recurring as of CLI 2.1.158, and seemingly server-side as well — see #54434 (stream stalls without
message_stop) and #63880 (delayed-batch tool-result delivery). A client-side read timeout like the one proposed here would at least bound the hang instead of leaving it open-ended.Adding a corroborating occurrence on a newer version than most reports in this thread.
Environment: Claude Code v2.1.167 (native install), Sonnet 4.6, Ubuntu 24.04, detached tmux, long-running session (~4h, heavy tool use).
Behavior observed: mid-turn, immediately after a large tool_result was returned (an API search response of ~178 items), the session parked permanently:
We investigated the host side before attributing this to the client: no network events, OOM, memory pressure, or DNS issues coincided with the hang, and other traffic from the same machine flowed normally through the window. Consistent with a silently stalled stream and no client-side read timeout.
If it recurs we should have per-turn client-side data to share (transcript persistence and an external liveness monitor are now in place).
Adding a data point and an OS-level mitigation that aren't in the thread yet.
Still present well past the "2.1.40 regression" window — and correlated with a macOS upgrade rather than a Claude Code version. Hitting Pattern 1 (mid-turn stream freeze) on Claude Code 2.1.168 (native build), macOS 26.5.1 (25F80), in a session over SSH + tmux with background subagents — i.e. the conditions described here. The relevant detail: it started the moment the machine was upgraded to macOS 26 and rebooted; it was not happening on the prior OS. During the hang the process sits at 0% CPU with the HTTP-client thread parked and no new transcript writes — blocked on a socket that's open but dead, exactly as reported.
That timing suggests this is less a Claude Code version regression and more an environmental trigger: macOS 26's network stack appears to drop idle long-lived connections without a clean FIN, and the missing stream read-timeout turns each such drop into an indefinite hang.
A mitigation that works without an app-side change (Pattern 1 only): macOS ships with
net.inet.tcp.always_keepalive=0andkeepidle=7200000(2h), so the kernel never probes a half-open socket. Claude Code already retries dropped connections (CLAUDE_CODE_MAX_RETRIES) but not a silently stalled stream — so enabling kernel keepalive converts the undetectable stall into a detectable drop, which the existing retry path then recovers:With these, a dead connection is detected in ~3 min instead of hanging forever. Caveat: this is grounded in the retry-on-dropped-connection behavior and our process analysis; it targets Pattern 1 (the dead-socket stream freeze) and will not help Pattern 2 (the tool-result delivery race), which isn't a socket-level stall. A client-side SSE read-timeout, as suggested above, remains the proper fix.
OS-level mitigation: aggressive TCP keepalive turns the infinite hang into a ~3-min auto-recovery
Hit Pattern 1 repeatedly on a Mac mini (Vertex, via SSH + tmux). Confirmed the process sits in
do_epoll_waitat 0% CPU — blocked on a socket read with no timeout, matching the OP.A contributing factor on macOS specifically: the default TCP keepalive is effectively off for this purpose —
So when the streaming socket dies mid-response, the kernel never probes it and the client waits forever. A client-side stream read timeout (as requested above) is still the right fix, but until then, making the kernel detect dead sockets is an effective workaround:
The kernel now declares a dead connection in ~3 min; the socket errors, and Claude Code's existing retry (
CLAUDE_CODE_MAX_RETRIES) recovers — infinite hang → ~3-min blip, nokill -9. Kernel-wide, so it's app-independent. To make it survive reboots, re-apply the sysctls at boot via a LaunchDaemon.This doesn't help a stall where the socket is still alive but no bytes flow (that genuinely needs the client read timeout), but it eliminates the dead-socket class of this bug.
Reproducing this on Windows + direct Anthropic API (not just Vertex/macOS) — adding frequency data and netstat forensics.
We run several long-lived Claude Code sessions as Windows services (NSSM, headless, Telegram plugin attached). We hit exactly this hang, and our data suggests it's not specific to Vertex AI, macOS, or tmux — we see it on Windows talking to the direct Anthropic API.
Symptom (matches the report):
claude.exestays alive but makes zero progress — no new tokens, the turn never completes. Process must be killed to recover.Forensics we captured on frozen processes:
netstaton the frozen PID shows the connection to the Anthropic API endpoint (...:443) still held — ESTABLISHED, and in some captures CLOSE_WAIT (server sent FIN, client never processed it). Consistent with a streaming read blocked indefinitely with no read timeout — exactly the root cause described here.Frequency / severity (single day, 4 instances):
Our workaround (we independently arrived at the same approach suggested here):
An external watchdog that detects staleness via the session's stdout/JSONL not advancing, then kills + restarts. Empirically our detector fires at a median of ~3.8 min of staleness. It works, but it's a blunt instrument: it kills the whole process and loses in-flight context, and a kill-storm can hit local restart caps.
Why a client-side read timeout would fix this cleanly: a configurable read/idle timeout on the SSE stream (the 2-minute threshold suggested above is reasonable) that surfaces a normal retryable error would let the client recover the turn in place, instead of every integrator building a process-killing watchdog. A keepalive/heartbeat on the stream would be an even better signal.
Happy to share minidumps of the frozen
claude.exe(we capture procdump at freeze time) if the stack traces would help pinpoint where the read blocks. Likely related: #33949.Environment: Claude Code (native Windows binary), Windows 10, direct Anthropic API (api.anthropic.com), multiple concurrent headless sessions.
Reproducing on native Windows 11 + first-party Anthropic API (not Vertex/Foundry), Opus 4.8 / CLI 2.1.170 — same tool_result-then-silence stall. Confirming the Vertex/Foundry-only idle-timeout fix doesn't cover first-party here.
Follow-up from the same Windows + first-party Anthropic API setup I posted from on Jun 11 — it's getting worse, and the recent idle-timeout fix doesn't seem to reach first-party.
api.anthropic.com): 37 soft-hang freeze→recovery events in a single day (2026-06-15), up from the 26/day I reported on Jun 11. Identical signature each time —claude.exealive at 0% CPU, the socket toapi.anthropic.com:443still held (ESTABLISHED / CLOSE_WAIT), zero new transcript writes, the turn never completes.--resumeand come back as fresh sessions with the in-flight context lost. The blast radius of "no read timeout" isn't one stalled turn — at volume it becomes repeated context loss across long-lived sessions.KeepAliveTimeregistry tuning and disabling IPv6, and neither resolved it — the socket stays ESTABLISHED (not dropped), so kernel keepalive never converts it into a retryable error.Ask: please extend the client-side SSE read/idle timeout already shipped for Vertex/Foundry to the first-party Anthropic API path as well. That one change would let the existing retry recover the turn in place and remove the need for every integrator to run a process-killing watchdog. Minidumps of the frozen
claude.exeavailable on request.@dudupstmaxx thanks for the corroboration — same setup here (native Windows 11, first-party API, Opus 4.8). Adding the application-side counterpart to your netstat/CPU forensics, plus the first request_id in this thread.
Caught one in a transcript (CLI 2.1.161): a
tool_resultreturned at04:42:37Z, then ~12 min of nothing until the Stop hook fired at04:54:49Z. Log-side detail: no follow-up generation request is ever issued — there's no request_id for the model turn that should have consumed the tool_result; it never starts. So the block may sit a step earlier than yours: your socket is held after the next request goes out (ESTABLISHED/CLOSE_WAIT, response never arrives); here it stalls before the next request is even emitted. Either way, same unbounded wait with no read/idle timeout on first-party to break it.Reads like serge-disruptive's Pattern 2 (tool_result-side stall, not the dead-socket Pattern 1) — a first-party instance, now with a request_id to correlate:
req_011CbqBPEY38MSQwXwaCFc7G+1 on extending the Vertex/Foundry idle-timeout to the first-party path — it would let the existing retry recover these in place instead of forcing a process kill.