Background agents (mobile/web bridge): hang indefinitely, auto-resurrect on SIGTERM, and edit a shared working directory unattended
Background agents (mobile/web bridge): hang indefinitely, auto-resurrect on kill, and edit a shared working directory unattended
Environment
- Claude Code 2.1.220 (native build, macOS app)
- macOS 26.5.2, arm64
- Model:
opus[1m] - Working directory: a local project, not a git repository (no
git checkoutrecovery path) - Background sessions were started from the Claude mobile app via the session bridge
(each transcript carries a bridge-session record with a cse_… id)
Summary
Four background agents and one interactive session were live in the same working
directory at the same time. I had no way to see or reach the background ones from the
machine they were running on, one of them hung for 40 minutes mid-task, and when its
process was killed the daemon silently restarted it and it kept editing my files.
Three separate problems, filed together because they compound each other.
---
Problem 1 — A background agent hangs after a tool result and is never surfaced
Session c099e939 ("Supplementierung: Soll-Definition & plan.py"), started 19:13:04.
Its last three transcript entries:
19:28:51 tool_use TaskUpdate task #3 -> completed
19:28:53 tool_use Bash "Final validation"
19:28:55 tool_result "GRÜN — keine Fehler (15 Warnungen). alle vier Skripte grün"
After that: nothing for 40 minutes. The transcript's last record is a tool_result
with no following assistant message. The process sat at 0.0 % CPU, state S (sleeping),
having used only 1:27 of CPU across 1 h 30 m of wall clock — it accumulated roughly
10 seconds of CPU over the 40 idle minutes, consistent with an idle heartbeat or a
connection retry loop, not with work.
Expected: a stalled agent either times out, retries visibly, or is reported as failed.
Actual: it stays busy forever with no indication anywhere that it is stuck.
Problem 2 — SIGTERM to a background agent silently resurrects it
With the agent idle and the working tree in a verified-clean state, I sent a plainSIGTERM (not -9) to its process:
kill 56345
The process exited. Three seconds later the daemon started it again:
20:09:40 claude --bg-pty-host … -- …/2.1.220 \
--resume …/c099e939-….jsonl \
--name "Supplementierung: Soll-Definition & plan.py" \
--model opus[1m]
and injected this prompt automatically:
Continue from where you left off. Note: this session was automatically restarted after its process exited.
The agent replied "Alles intakt. Weiter mit den …" and resumed editing the project.
It re-claimed the same control socket (rv/c099e939.sock) and carried on withpermissionMode: auto, i.e. writing files without prompting.
Expected: killing a background agent's process stops that agent, or at minimum the
restart is surfaced to the user and requires confirmation.
Actual: an explicit user attempt to stop an agent is silently undone, and the agent
resumes making unattended writes. From the outside this is indistinguishable from the
kill having failed.
claude agents does list it correctly (status: busy), and is presumably the supported
way to stop it — but nothing in the kill path hints at that, and the restart happens
regardless of user intent.
Problem 3 — Bridged background agents are invisible and unreachable from the host
The agents were started from the Claude mobile app. On the machine actually running them:
- They do not appear in the interactive session in any way.
- The user could not open, inspect, or stop them from the phone either — the app had lost
its handle on them.
claude agents --jsonwas the only thing that surfaced them, and only because I went
looking for it. claude --help mentions it exclusively in the --bg flag description.
State of this one project when I looked:
| Name | Status |
|---|---|
| Supplementierung: Soll-Definition & plan.py | busy (the hung one) |
| Supplementierung: Preis-Refresh | waiting — "input needed" |
| Supplementierung: Bedarfs-Modus + Medikationsfeld | waiting |
| Supplementierung: D3-Kandidaten via medizinfuchs | blocked |
Three agents had been sitting on an input prompt for hours that nobody could see or answer.
---
Why the combination is worse than the parts
Two sessions — one interactive, one background — were editing the same non-git directory
concurrently, both under permissionMode: auto. I watched a source file grow by 600 bytes
between two reads and briefly observed it in a non-runnable state
(NameError: name 'basis' is not defined) while a refactor was half applied.
Recovery would have depended on file-history-snapshot, but the entries recorded for those
writes had trackedFileBackups: {} — empty. With no git and no backups, a concurrent
overwrite would have been unrecoverable.
Suggested fixes
- Time out or fail an agent that has not progressed after a
tool_result; surface stalled
agents in claude agents as something other than busy.
- Do not auto-
--resumean agent whose process received aSIGTERM. Distinguish a crash
from a deliberate termination, and make the restart visible when it does happen.
- Warn when a background agent's working directory overlaps a live interactive session,
especially outside a git repository.
- Populate
trackedFileBackupsfor writes made by background agents. - Make bridged agents reachable from the host session — and make
claude agents
discoverable beyond a mention inside the --bg help text.
Reproduction
Not reliably reproduced on demand; the hang appeared spontaneously. The auto-restart
behaviour in Problem 2 is deterministic: kill the process of any running background agent
and observe the daemon respawn it with --resume within a few seconds.