macOS: background agent workers reaped mid-work (reapedMidWorkAt), never respawn — refines #72221
Summary
Following up on #72221 with more precise, freshly-captured evidence — some of which contradicts the "2 concurrent agents signal war" theory from that issue and points at a more specific mechanism: background-agent worker processes are being killed mid-work by what looks like an orphan/idle watchdog, and dead workers are never respawned through the documented respawn path. Recovery (when it happens) is incidental, via an unrelated "spare pool" claim, and can take several minutes — which is what surfaces to users as "my session just stopped for no reason."
Environment: macOS (Apple Silicon, Darwin 25.5.0), Claude Code 2.1.198, Agents View / background-job (FleetView) sessions.
Live, reproduced-in-the-wild evidence (captured today)
While actively debugging this exact issue in a live Agents View session, the session itself was hit by the bug:
- The job's own
~/.claude/jobs/<id>/state.jsonrecorded:
````
"reapedMidWorkAt": "2026-07-02T08:39:57.333Z"
at a moment where the session was doing legitimate work (waiting on a single long-running subagent tool call, ~8.7 minutes, with no terminal I/O in that window).
~/.claude/daemon.logshows, ~5 minutes later:
````
[2026-07-02T08:44:57.901Z] [bg] bg adopt: adopted=2 respawned=0 dead=4
[2026-07-02T08:44:58.093Z] [bg] bg claimed-spare fed84255 (fleet)
i.e. the job was one of 4 simultaneously-dead workers, and only resumed because an unrelated spare-pool claim happened to pick the same job id back up. Nothing about this recovery was guaranteed or fast — it just happened to occur ~5 min later.
- This matches an
exit-causestring (ptyhost_orphan_watchdog) found recorded verbatim on 3 other, separate job directories from the same machine — a pty-host process reaping sessions it judges "orphaned."
Why this refines (not just repeats) #72221
#72221's working theory was that 2+ concurrently running agents fight over daemon ownership and that's what kills workers (cause=signal, leases=2). Fresh log analysis on this machine does not support that as the primary kill mechanism:
- The ~52s transient-daemon restart churn described in #72221 is happening continuously, regardless of load — including a 4-hour window with
workers=0(no agents running at all) that still showed a restart roughly every 52s. - During that entire 4-hour, ~280-sample restart storm,
dead=0on every single sample — the restart churn itself never killed a worker. - Across the full 7-week
daemon.loghistory (3,887bg adoptlog lines),respawnedhas never once been nonzero, even on the (much rarer, roughly a few times per day) samples wheredead>0. So workers really do die independently of the restart churn, and the daemon's own respawn path appears to never fire.
Other corroborating findings from the same investigation
- Silent-looking failures that are actually usage/session limits: one job's
timeline.jsonlshowed twostate:"failed", detail:"Continue"entries with empty text, immediately followed bystate:"blocked", detail:"You've hit your session limit · resets 12:40am (America/Los_Angeles)". The real reason only surfaced after two uninformative silent-looking retries — from the user's side this looks identical to a random freeze. - Agents View (Electron) renderer polling stalls when unfocused:
~/Library/Logs/Claude/main.logshowed[SkillsPlugin] Window focused — polling now (last poll was 18527215ms ago)(~5.15 hours stalled), followed immediately by an 11-error burst ofTypeError: Failed to fetch+ anAbortErrorinRemotePluginManageron refocus (claude.ai-web.log). This is a plausible source of the displayed agent status desyncing from the real backend state, independent of the daemon-side kill issue above. - Ruled out on this machine (with direct evidence, happy to share full logs): OS-level kill/jetsam/thermal events targeting
claude/nodeprocesses, network/auth/rate-limit errors, and version skew or auto-update timing.
Ask
- Either make the documented respawn path for dead background workers actually fire, or — if a worker is killed intentionally by the orphan-watchdog — surface that as a clear status ("worker reaped, resuming…") instead of the session just going silent.
- Reconsider the orphan/idle-watchdog heuristic for sessions that are legitimately waiting on a long-running subagent/tool call rather than truly idle.
- Surface
blockedreasons (e.g. session-limit resets) immediately rather than after one or two silentfailed | Continueretries.
Happy to share raw daemon.log / state.json / timeline.jsonl excerpts on request.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗