/exit warns about running background agents when none are running; agent sidecars never record completion
Environment
- Claude Code
2.1.200 - macOS (Darwin 25.3.0), arm64
- Install path:
~/.local/bin/claude
---
Summary
After spawning any subagent via the Agent tool during a session, /exit warns that background agents are still running, even when every spawn was foreground (run_in_background: false, the default) and every result has already been returned to the main loop. Users have no way to see, list, or stop the agents the warning is referring to — they are, by every observable signal, already done.
The root appears to be that per-agent metadata written under ~/.claude/projects/<project>/<session>/subagents/agent-*.meta.json is written once at spawn and never updated. There is no status, endedAt, exitCode, completed, or any other terminal-state field, so nothing on disk distinguishes "returned six hours ago" from "still executing."
---
Reproduction
- Start a fresh session in any project.
- Use the
Agenttool once (defaults — foreground,run_in_background: false). Any agent type; any prompt. Wait for the tool result to return in the transcript. - Type
/exit.
Expected: clean exit, no warning (nothing is running).
Actual: warning that background agents are still active, with a count.
The count grows with every additional Agent call in the session, regardless of whether any of them were ever backgrounded.
---
Evidence from an affected session
Inspecting ~/.claude/projects/<project>/<session>/subagents/:
- 102
agent-*.meta.jsonsidecar files present. - 102 matching
agent-*.jsonltranscript files present. - 0 foreground/background distinction on disk — the tool argument
run_in_backgroundis not reflected in the sidecar. - 0 files contain any terminal-state field.
Schema across all 102 files:
agentType: 102
description: 102
spawnDepth: 102
toolUseId: 85 # anonymous Agent() spawns
name: 17 # named/addressable spawns
taskKind: 17 # "in_process_teammate"
teamName: 17
color: 17
planModeRequired: 17
model: 17
permissionMode: 17
Terminal-state fields present: False
Representative meta contents (an agent whose result was returned to the main loop hours before /exit):
{
"agentType": "<some-agent-type>",
"description": "<one-line task summary>",
"toolUseId": "toolu_bdrk_...",
"spawnDepth": 1
}
Same shape for every agent in the directory. Nothing on disk changes when the agent finishes.
---
What appears to be broken (one or more of these)
- Sidecar metadata never records completion.
.meta.jsonis write-once at spawn. Whatever check/exituses to compute the "running" count has no ground truth to consult.
/exit's "is running" filter is wrong. Even if terminal state lives somewhere else (in-memory only, or in the top-level session JSONL), the exit warning is counting sidecar files (or an equivalent addressable-agent set) rather than actual live process/task handles.
- User has no surface to inspect or reconcile. There is no visible list of "these agents are believed to be running" and no command to clear the phantom set. The warning cannot be acted on; users are left choosing between ignoring it (and risking real stranded work someday) or force-quitting.
---
Secondary observation — unbounded sidecar growth
Because completion is never recorded and sidecars are never pruned, subagents/ grows without bound across the life of a session. Long-lived sessions accumulate hundreds of transcripts totaling ~100MB+, indistinguishable to any tool from live work. This makes any "running count" that treats these files as authoritative permanently wrong.
---
Suggested direction (non-prescriptive)
Any one of these would resolve the false positive:
- Update the sidecar (or write a companion
.donemarker) when the Agent tool returns its result to the parent. - Have
/exitcheck active process/task handles instead of persisted sidecar files. - Expose a
/agentsor/taskscommand that lists whatever/exitbelieves is running, so users can see and reconcile the discrepancy.
The first two prevent the false positive; the third at least makes it debuggable.