claude-code - 💡(How to fix) Fix claude -p hangs indefinitely on a silently-dead API stream (no SSE stream-idle timeout)

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…

A non-interactive claude -p invocation can hang indefinitely when its connection to api.anthropic.com dies silently mid-stream (no FIN/RST — e.g. a NAT/middlebox idle-timeout during a long model turn). claude never detects the dead SSE stream; it produces zero output and never exits until killed by an external wall-clock timeout.

Root Cause

API_TIMEOUT_MS (default 600000 ms) is a total-request timeout. It did not fire on this mid-stream stall — the hung process held the same two sockets across observations 4+ minutes apart, both well past the 10-minute mark. There appears to be no stream-idle timeout, and CLAUDE_CODE_MAX_RETRIES never engages because the request never fails — it just hangs.

RAW_BUFFERClick to expand / collapse

Summary

A non-interactive claude -p invocation can hang indefinitely when its connection to api.anthropic.com dies silently mid-stream (no FIN/RST — e.g. a NAT/middlebox idle-timeout during a long model turn). claude never detects the dead SSE stream; it produces zero output and never exits until killed by an external wall-clock timeout.

Environment

  • claude 2.1.143 (Claude Code), macOS
  • Invocation (autonomous / headless): claude -p "<prompt>" --model opus --effort high --max-budget-usd 20 --dangerously-skip-permissions, stdin = /dev/null

Forensics on a hung process

Captured live on a claude -p that had produced zero output for 30+ minutes:

  • ps: STAT=Ss, ~0% CPU — a brief startup CPU burst, then asleep. Not computing.
  • No child processes — not a hung tool subprocess; the wedge is inside claude.
  • lsof: two ESTABLISHED IPv6 sockets to api.anthropic.com (2607:6bc0::10:443).
  • netstat: those sockets show Send-Q 0 / Recv-Q 0 — the request was fully sent; nothing is coming back.
  • sample: the HTTP Client thread is parked 100% in kevent64; the main event loop is idle.
  • A fresh curl to api.anthropic.com (IPv4 and IPv6) connects + responds in ~10 ms — the network path is healthy. Only the long-lived connection claude is holding has gone stale.

API_TIMEOUT_MS does not cover this

API_TIMEOUT_MS (default 600000 ms) is a total-request timeout. It did not fire on this mid-stream stall — the hung process held the same two sockets across observations 4+ minutes apart, both well past the 10-minute mark. There appears to be no stream-idle timeout, and CLAUDE_CODE_MAX_RETRIES never engages because the request never fails — it just hangs.

Expected behavior

claude -p should detect a stalled SSE stream — no bytes received for a (configurable) interval — and abort + retry. A fresh connection works immediately, so the existing retry path would recover the request. Today it waits forever.

Why it matters

For any automated/headless use of claude -p (CI, agents, scheduled jobs), a single dead-stream event is an indefinite hang that only an external timeout wrapper can break. A stream-idle timeout would turn an unrecoverable hang into a recoverable transient.

Request

Add an SSE stream-idle timeout to the API client used by claude -p — abort + retry a stream that has received no bytes for a configurable interval (e.g. an API_IDLE_TIMEOUT_MS env var).

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…

FAQ

Expected behavior

claude -p should detect a stalled SSE stream — no bytes received for a (configurable) interval — and abort + retry. A fresh connection works immediately, so the existing retry path would recover the request. Today it waits forever.

Still need to ship something?

×6

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

Back to top recommendations

TRENDING