Monitor until-loop leaks a shell that spins indefinitely when its exit condition never matches (survives task purge & /compact)
What happened
A Monitor started with a polling until-loop never exited because its grep condition never matched, leaving a shell spinning sleep 3 for 10h+. The shell counter (N shell still running) was stuck at 1 and could not be reconciled or stopped programmatically.
The Monitor's command (from the Shell details view):
f=…/tasks/a2213d84c8e8a4938.output
until [ -f "$f" ] && grep -q '"type":"result"' "$f" 2>/dev/null; do sleep 3; done; echo done
It was watching an agent's .output file for a "type":"result" marker. The agent completed and its task was purged, but the .output never contained that literal string, so the loop spun forever.
Shell details showed: Status: running · Runtime: 10h 2m 27s.
Expected
A Monitor whose watched task/file no longer exists (purged), or that exceeds its timeout_ms, should self-terminate and decrement the running-shell counter.
Actual
- The loop spins
sleep 3indefinitely (observed 10h+). - It's invisible to a process scan filtered on
pytest|uv|python(it's a bash sleep-loop), so it's easy to misdiagnose as "nothing running." - The assistant cannot stop it:
TaskStop/TaskOutputon the watched id returnsNo task found with ID: …(purged), and the Monitor's own task id is not surfaced to the model — only the user can kill it via↓ to manage → x. - It persisted across a
/compact.
Repro (approx.)
- Start a
Monitorwith a pollinguntil-loop whose exit condition may never match (e.g. grepping an agent transcript for a string that isn't written in that form). - Let the watched task complete and get purged.
- Observe the Monitor keeps running indefinitely; the shell counter stays at 1; querying the watched id yields "No task found"; a
pytest|pythonpsscan shows nothing.
Suggested fixes
- Reap Monitors whose watched task/file no longer exists, or whose
timeout_mshas elapsed. - Surface the Monitor's own task id to the model so it can be
TaskStop-ped programmatically. - (Docs/guardrail) Discourage
Monitorpolling-loops for awaiting agents — agent completions are delivered via notifications.
Environment
- Claude Code
2.1.177 - macOS (Darwin 24.6.0), zsh
- Long session (~577k tokens), post-
/compact, auto mode on
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗