Background daemon: fresh-spawned (non-spare) --bg workers crash "exit N before init" 100% of the time

Status Open
Reported on v2.1.212
Maintainer reply None cached
Activity 0 comments · opened Aug 27, 2026

Version: 2.1.212 (also observed on 2.1.14x-2.1.15x daemons; macOS 14, Darwin 23.6.0)

Summary

The background daemon keeps one pre-warmed spare worker. Jobs that claim the spare (bg claimed-spare <id> in daemon.log) succeed. Jobs dispatched through the fresh-spawn path (bg spawned <id>) crash roughly 13 seconds later with bg settled <id> (crashed): exit N before init, before session init, every single time. In my full daemon.log history: 394 spare-claimed jobs, almost all successful; 16 fresh-spawned jobs, 16 crashed. Zero fresh-spawn successes on record.

Reproduction conditions

A job lands on the fresh-spawn path in two cases, both reproducible on my machine:

  1. Two claude --bg dispatches arrive in the same second. The first claims the spare and succeeds; the second fresh-spawns and crashes. Observed daily when two scheduled jobs fire at the same minute; the crashing job alternates depending on which dispatch wins the race.
  2. Cold daemon start (no spare exists yet). The dispatching job fresh-spawns and crashes.

Sample log window (2026-08-27, UTC)

14:00:05.726 [bg] bg claimed-spare d8fe41c4 (shell)   <- succeeded (ran 7m, done)
14:00:05.745 [bg] bg spawned f6eba5ef (shell)         <- fresh spawn
14:00:05.760 [bg] bg spare spawned host pid=16857
14:00:18.794 [bg] bg settled f6eba5ef (crashed): exit 1 before init

Auth was healthy in the same window (auth: scheduling proactive refresh in 28560s, no failure lines), so this is not the headless OAuth-refresh issue. Immediately after past occurrences, direct claude -p and claude --bg probes with the identical flag set completed clean, so flags, model, prompt, and credentials are not the cause.

Impact

Any consumer that schedules more than one background job at the same minute, or dispatches into a cold daemon, deterministically loses a job. The worker's stderr is not observable from the dispatching side (the launcher's pipes are destroyed at the backgrounded banner), so the only diagnostic available is the exit N before init detail string.

Ask

Either fix the fresh-spawn worker path, or have the daemon queue the second dispatch until the replenishment spare is ready, or surface the worker's early stderr in state.json so consumers can see why init failed.

View original on GitHub ↗