Background agent whose cwd was deleted crash-loops (exit 1 — exit_with_message) and is re-adopted on every daemon restart
What happened
A background agent (bg session) whose working directory was later deleted gets re-adopted by the daemon on every restart and immediately crash-loops with:
[worker crashed (exit 1 — exit_with_message) — respawning…]
The worker cannot chdir into its recorded cwd (the directory no longer exists), exits 1, the supervisor respawns it, and it fails again — typically 4–5 times within a few seconds. It is eventually marked dead, but the job record survives, so the next daemon restart re-adopts it and the loop repeats. In practice this surfaces as "worker crashed … respawning" appearing several times a day.
This is common with git worktrees: you start a bg session inside a worktree, finish the work, delete the worktree, and the session's ~/.claude/jobs/<id>/state.json still points at the now-deleted path.
Steps to reproduce
- From directory
D, start a background agent (so a record is written to~/.claude/jobs/<id>/state.jsonwith"cwd": "D"). - Delete
D(e.g. tear down the git worktree, orrm -rfa scratch dir). - Restart the daemon (or wait for the next adopt cycle).
Expected
A job whose cwd no longer exists is a terminal condition: mark it permanently dead / drop the record on the first chdir ENOENT. Do not re-adopt it on subsequent restarts.
Actual
The record persists and is re-adopted every restart, producing a fast crash→respawn loop (exit 1 — exit_with_message) each time until it self-marks dead again.
Evidence (~/.claude/daemon.log)
Four crash-and-respawn events ~66 ms apart:
[…T21:07:04Z] [bg] bg adopt: adopted=6 respawned=0 dead=0
[…T21:07:16.919Z] [bg] bg settled <id> (crashed)
[…T21:07:18.174Z] [bg] bg settled <id> (crashed)
[…T21:07:19.826Z] [bg] bg settled <id> (crashed)
[…T21:07:21.382Z] [bg] bg settled <id> (crashed)
The ~66 ms crash interval is far too fast for model/tool work — consistent with an immediate spawn-time failure (chdir into a missing cwd).
Impact
Self-limiting (no live work is lost — the crashers are finished/orphaned sessions), but it produces recurring terminal noise and wasted spawn churn, and is confusing because the crash reason is printed only to the terminal, not written to daemon.log (so the underlying cause isn't recoverable after the fact).
Environment
- Claude Code 2.1.201 (also seen on 2.1.199)
- Observed on macOS and inside a Linux container;
bg(background agent) sessions - Host/pod-level restart count is 0 — this is purely the daemon respawning workers, not the host restarting.
Suggested fixes
- On worker spawn, if the recorded
cwddoes not exist, treat it as terminal: mark the job dead and stop re-adopting it (don't respawn, don't resurrect on the next restart). - Persist the worker's exit reason/message to
daemon.log(or the job'sstate.json) soexit_with_messagecrashes are diagnosable after the fact — right now the reason is only ever shown transiently in the attached terminal.