[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

Status Open
Reported on v2.1.241
Maintainer reply None cached
Activity 0 comments · opened Aug 25, 2026

Preflight Checklist

  • [x] I searched existing issues. Closest matches: #27423 (orphan SubagentStop with empty agent_type and no matching SubagentStart — stale-closed, not fixed) and #82249 (SubagentStop never 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 empty agent_type, consistently 2–7 seconds after the parent Stop hook, 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_id that never appeared in any SubagentStart, 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 SubagentStop carries the same agent_type as its SubagentStart.
  • 7 of 7 orphan stops with empty agent_type arrived 2–7 s after a parent Stop (some in turns with a pending started subagent, some in turns without one).
  • Orphan stops that do carry an agent_type were all explainable by log truncation on our side (their starts predate the log window), so the malformed class is precisely: unknown agent_id + empty agent_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 extracts session_id, hook_event_name, agent_id, agent_type from stdin JSON and appends them to a local log (no transport in between), so the mismatch is present in the raw hook payloads.

View original on GitHub ↗