Desktop app: message hangs pre-first-byte (hadFirstResponse=false); stop kills worker, synthesizes phantom permission denials; pcap proves request never transmitted

Resolved 💬 0 comments Opened Jul 13, 2026 by alextmacpaw Closed Jul 14, 2026

Environment

  • Claude desktop app 1.20186.1 (macOS, Darwin 27.0.0, Apple Silicon)
  • Bundled CLI: 2.1.205 (~/Library/Application Support/Claude/claude-code/2.1.205/) — note: a clean terminal reinstall of 2.1.207 does not affect desktop sessions, which spawn the bundled binary
  • Terminal-launched claude sessions: unaffected
  • Work-managed Mac with EDR; network fully exonerated (see packet-capture proof below)

Summary

Desktop-app sessions intermittently stop delivering user messages: the first message is answered, a later message (typically after minutes of idle) spins indefinitely with no error. Pressing stop cannot abort the stalled query and instead kills the whole worker process, with serious collateral damage (details below). Reproduced across projects, networks (home Wi-Fi + phone hotspot), and a clean reinstall. Per-session lottery: two sessions created minutes apart in the same empty folder behave differently.

Mechanism (from ~/Library/Logs/Claude/main.log)

Captured live twice (11 Jul and 13 Jul 2026), identical chain:

16:50:59 LocalSessions.sendMessage (59 chars)              <- message accepted
16:52:31 LocalSessions.interrupt                            <- user hits stop after 93s spinner
16:52:31 [CCD CycleHealth] healthy cycle (93s, hadFirstResponse=false)
16:52:33 [warn] query.interrupt() did not resolve within 1500ms (hadFirstResponse=false);
         falling back to stopSession. SDK abort not honoured pre-first-byte — see withRetry.
16:52:33 Stopping session ...
16:52:33 [CCD] Session ... torn down with 1 live background task(s) — settling as stopped

Packet-capture proof: the request is never transmitted

A tcpdump capture (13 Jul, 16:49:36–16:53:08) covers the entire 93-second hang above:

  • After sendMessage at 16:50:59, traffic to api.anthropic.com (160.79.104.10) is pure keepalives: 0-data ACK pairs every second, ~30-byte HTTP/2-level pings every ~10 s, totalling ~63 KB over 93 s. No request upload to any Anthropic endpoint, TCP or QUIC.
  • The worker's existing connections were alive and actively kept alive the whole time.
  • The same window shows ~5 MB flowing to other hosts — the machine and network were fully healthy.
  • Node-stack probes from the same machine: 20/20 fresh connections (IPv4+IPv6) in 126–302 ms; a keep-alive socket idle 150 s reused instantly.

The message is lost inside the app/SDK before request transmission — consistent with the app's own warning that the stall is pre-first-byte in withRetry. Earlier worker debug logs from hung sessions additionally show the session-bridge SSE stream (/v1/code/sessions/<id>/worker/events/stream) taking 31 s to establish (one full timeout + retry) and dying with epoch superseded (409) at teardown, with messages sent to the dead epoch stranded.

Collateral damage of the stopSession fallback (all observed live)

When the interrupt fallback kills the worker:

  1. Pending tool-permission requests are auto-answered as "The user doesn't want to take this action" — the user never declined. These phantom denials corrupt the transcript and mislead the agent (it believes the user rejected its action).
  2. Running background tasks and Workflow runs are killed with no completion record ("torn down with N live background task(s)").
  3. All MCP server connections drop simultaneously.
  4. getContextUsage / deferred setMcpServers fail with Query closed before response received (stack: teardownQuery -> teardownSession -> stopSession -> interruptSession).

Requests

  1. Honour abort pre-first-byte (the withRetry reference in the app's own warning), so stop doesn't have to kill the worker.
  2. Fix the pre-request stall itself: hadFirstResponse=false for 93 s while the wire shows the request never left the machine.
  3. On epoch superseded (409), re-deliver in-flight user messages to the new epoch instead of stranding them.
  4. Worker teardown must not synthesize "user declined" answers to pending permission requests.
  5. Surface a visible error/retry in the UI instead of an indefinite spinner.

Session IDs available for server-side correlation on request; full main.log excerpts, worker debug logs, and the pcap are retained and can be provided.

View original on GitHub ↗