Headless claude -p hangs indefinitely before first response — CLOSED sockets held, no timeout/retry, zero output even with --debug
Summary
Scheduled headless jobs (claude -p via launchd on macOS) hang forever before producing any output. The process sits at 0% CPU holding TCP connections to the Anthropic API that the OS has already marked CLOSED. The CLI never detects the dead connections, never retries, never errors, and produces zero stderr output — even with --debug, which wrote literally 0 bytes over 20+ minutes. The session transcript shows the user prompt recorded and then no assistant events at all.
Interactive sessions on the same machine, same network, same account work fine throughout.
Environment
- macOS (Apple Silicon)
- Claude Code 2.1.215 and 2.1.216 (both reproduce)
- Subscription auth (individual account), not API key
- Jobs launched via launchd and manually from a shell — both reproduce
Reproduction pattern (from ~2 weeks of nightly runs + one evening of controlled tests)
Two nightly headless jobs (a ~10KB-prompt "triage" job pinned to claude-opus-4-8 with MCP tools allowed, and a smaller sync job on claude-sonnet-4-6 using MCP tools) began hanging intermittently ~2026-07-11 and consistently 2026-07-18 through 2026-07-20 (US evening hours, ~5:45pm CT). Each attempt was killed by our wrapper at a 20-minute timeout; 3 attempts/night, all identical.
Controlled differentials run 2026-07-20 evening (all same machine/account):
| Test | Result |
|---|---|
| claude -p "Reply OK" (tiny prompt, sonnet) | ~6s ✅ |
| Tiny prompt, opus-4-8, MCP tools in --allowedTools, cwd / | ~4s ✅ |
| Full 10KB prompt, opus-4-8, but instructed to reply with one line ("TEST OK") | seconds ✅ |
| Exact production invocation (same session-id style, full allowedTools incl. connector MCP tools) + one-line-reply override | seconds ✅ |
| Full prompt, real work (model must think + make tool calls), opus-4-8 | hung ≥20 min, killed ❌ (reproduced 7×) |
| Same real-work run on sonnet-4-6, overlapping the same minutes as a hung opus run | completed in 19 min ✅ |
Key differential: identical invocation, trivial reply → always fast; real first working turn → hangs. Opus-4-8 hung 19:18–19:38 CT while sonnet-4-6 ran the identical job successfully 19:25–19:44 CT.
Forensics on live hung processes
- 0% CPU, state S — blocked, not spinning
lsof -a -p <pid> -i: 7 TCP connections to 160.79.104.10:443, all in CLOSED state, FDs never released, no new connections being opened (no retry)- One unreaped
<defunct>child --debugrun: 0 bytes of output after minutes of hanging- Session transcript (
projects/.../<session>.jsonl): user prompt + attachments written, then zero assistant events - MCP servers all connected fine (claudeai-proxy connectors connect in <1s per their logs); no hook processes stuck
Hung session IDs + timestamps (UTC), if you can match server-side logs
- 2026-07-20 22:45:11Z — e0a5820f-0d74-4db7-8709-be658b16f30e (opus-4-8, launchd)
- 2026-07-20 23:05:53Z — c9aa9b39-f374-4c44-b2d4-54b877634c3e (opus-4-8, launchd)
- 2026-07-20 23:28:04Z — b5edbb7f-7559-4796-8b6c-ce42cc055a95 (opus-4-8, launchd)
- 2026-07-20 23:57:55Z — 4e086893-ba7f-4356-8630-3a408ce3d11a (opus-4-8, manual)
- 2026-07-21 00:18:31Z — 684723af-d4da-47c2-a3d4-87190cbf7d67 (opus-4-8, manual)
- 2026-07-19 22:45:07Z — 663666a9-89d1-44f7-bd99-994499da9e63 (opus-4-8, launchd)
- Successful sonnet run for contrast: 16686c0b-5510-4855-b66c-3263cfdec24c (2026-07-21 ~00:25Z)
Two distinct asks
- The hang itself — why do real-work first requests on opus-4-8 die in US evening hours for this account/machine while trivial requests succeed?
- The CLI's failure mode — regardless of cause,
claude -pshould not wait forever on a connection the OS has marked CLOSED. A dead-connection detection + retry (or at minimum an error + nonzero exit) would have made this a blip instead of three nights of silent data loss. Print mode also emitting nothing under--debugmade this much harder to diagnose than it should have been.
We're running a socket-state timeline logger on tonight's scheduled run and can attach the connection-death timeline when it fires.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗