macOS: background agent workers reaped mid-work (reapedMidWorkAt), never respawn — refines #72221

Open 💬 2 comments Opened Jul 2, 2026 by Haohaokankan123

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.json recorded:

``
"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.log shows, ~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-cause string (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=0 on every single sample — the restart churn itself never killed a worker.
  • Across the full 7-week daemon.log history (3,887 bg adopt log lines), respawned has never once been nonzero, even on the (much rarer, roughly a few times per day) samples where dead>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.jsonl showed two state:"failed", detail:"Continue" entries with empty text, immediately followed by state:"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.log showed [SkillsPlugin] Window focused — polling now (last poll was 18527215ms ago) (~5.15 hours stalled), followed immediately by an 11-error burst of TypeError: Failed to fetch + an AbortError in RemotePluginManager on 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/node processes, network/auth/rate-limit errors, and version skew or auto-update timing.

Ask

  1. 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.
  2. Reconsider the orphan/idle-watchdog heuristic for sessions that are legitimately waiting on a long-running subagent/tool call rather than truly idle.
  3. Surface blocked reasons (e.g. session-limit resets) immediately rather than after one or two silent failed | Continue retries.

Happy to share raw daemon.log / state.json / timeline.jsonl excerpts on request.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗