[BUG] SubagentStop can arrive with a different agent_id than SubagentStart (fresh id, empty agent_type, 2-7 s after parent Stop), leaving started subagents unmatched
Preflight Checklist
- [x] I searched existing issues. Closest matches: #27423 (orphan
SubagentStopwith emptyagent_typeand no matchingSubagentStart— stale-closed, not fixed) and #82249 (SubagentStopnever fires for Agent-tool async subagents — open). This report adds a new datum that ties the two together: the orphan stop arrives with a fresh agent_id and emptyagent_type, consistently 2–7 seconds after the parentStophook, in the same turn where a started subagent's own stop went missing. - [x] Single bug report
- [x] Reproduced on Claude Code 2.1.241
What's Wrong?
With SubagentStart / SubagentStop hooks that log session_id, hook_event_name, agent_id, agent_type, some subagents never receive a SubagentStop under the agent_id their SubagentStart carried. Instead, shortly (2–7 s) after the parent turn's Stop hook, a SubagentStop fires with:
- an
agent_idthat never appeared in anySubagentStart, and - an empty
agent_type.
Any consumer that pairs subagent lifecycles by agent_id (the documented identity) permanently leaks an "active" subagent.
Observed timeline (one session, UTC, ids truncated)
14:07:56 UserPromptSubmit
14:09:43 SubagentStart agent_id=a9ccdb5… agent_type=other
14:09:44 SubagentStart agent_id=aefcb63… agent_type=other
14:09:46 SubagentStop agent_id=a9ccdb5… agent_type=other (matched)
14:09:53 SubagentStop agent_id=aefcb63… agent_type=other (matched)
14:09:53 SubagentStart agent_id=a8fd658… agent_type=other <- never gets a stop under this id
14:11:28 SubagentStart agent_id=a3882ae… agent_type=other
14:12:09 SubagentStop agent_id=a3882ae… agent_type=other (matched)
14:15:45 Stop
14:15:50 SubagentStop agent_id=a118fc6… agent_type="" <- orphan: no Start, no type, +5 s after Stop
Aggregate signature (one-hour hook log, 3 sessions, 15 starts / 26 stops)
- Every id-matched
SubagentStopcarries the sameagent_typeas itsSubagentStart. - 7 of 7 orphan stops with empty
agent_typearrived 2–7 s after a parentStop(some in turns with a pending started subagent, some in turns without one). - Orphan stops that do carry an
agent_typewere all explainable by log truncation on our side (their starts predate the log window), so the malformed class is precisely: unknownagent_id+ emptyagent_type.
Expected Behavior
SubagentStop fires with the same agent_id as the corresponding SubagentStart, and carries the same agent_type. If turn teardown emits stop events for internal agents that never fired SubagentStart (see #27423), that would be worth documenting so consumers can distinguish them; but a started subagent should still get a stop under its own id (see #82249).
Environment
- Claude Code 2.1.241
- macOS (darwin 25.6.0)
- Hooks registered in
~/.claude/settings.json; the hook script only extractssession_id,hook_event_name,agent_id,agent_typefrom stdin JSON and appends them to a local log (no transport in between), so the mismatch is present in the raw hook payloads.