Backgrounding an interactive conversation fails on daemon cold-start (race between spawn IPC and control socket bind) — 2.1.220
Summary
Backgrounding an interactive conversation (background/park gesture, internal left_arrow path) deterministically fails when the background daemon is cold: the CLI persists state: "failed" ("couldn't start in the background — press Enter to retry") 7–42 ms after the request, 166–692 ms before the auto-started transient daemon finishes binding its control socket.
Environment
- Claude Code 2.1.220 (also reproduced the same code path string in 2.1.218/2.1.219 binaries)
- macOS (Darwin 25.5.0, arm64)
- Third-party Anthropic-compatible endpoint via env-injected token (no keychain OAuth);
ANTHROPIC_AUTH_TOKENpresent in daemon env
Reproduction
- Let the background daemon idle-exit (
idle 5s with no clients — exitingindaemon.log). - In an interactive session, background/park the conversation.
- Observe: the job is instantly marked failed with "couldn't start in the background — press Enter to retry".
Evidence (3/3 occurrences, timestamps from jobs/*/state.json vs daemon.log)
| Job createdAt | failed written | transient daemon start logged | control socket bound | gap |
|---|---|---|---|---|
| 15:19:43.265 | 15:19:43.288 (+23 ms) | 15:19:43.470 | 15:19:43.660 | failed 372 ms before bind |
| 15:23:12.440 | 15:23:12.447 (+7 ms) | 15:23:12.613 | 15:23:12.718 | failed 271 ms before bind |
| 23:06:26.304 | 23:06:26.346 (+42 ms) | 23:06:26.979 | 23:06:27.038 | failed 692 ms before bind |
In all three cases the failure was recorded before the freshly spawned daemon even logged its start line. Jobs created later against the already-warm daemon succeed.
Code path (from binary, v2.1.220)
The left_arrow background path calls the spawn routine and, on failure, persists the failed job record and copies the live transcript (this is the press Enter to retry flow). The spawn call appears to race daemon startup: when no daemon is running, the CLI triggers a transient daemon start but the spawn IPC rejects immediately instead of waiting for the control socket to accept connections (~250–700 ms needed).
A queued background spawn (slash-command path) does not hit this: the daemon picks the job up after boot — suggesting the interactive park path lacks the same readiness wait/retry.
Expected
The park path should either wait for daemon readiness (with a short bound, e.g. retry-until-bound up to ~2 s) or queue the job for the daemon to claim after startup, like the slash path does.
Actual
Instant failure + failed job record; user must manually retry (Enter), which succeeds once the daemon is warm.
Impact / workaround
Every park attempt after an idle period fails on first try. Workarounds: keep a background job resident so the daemon never idle-exits, or retry immediately after the failure (daemon is warm by then).
Data is not lost: the transcript is copied into the job dir before marking failed, so retry restores the conversation.