Hooks docs: stop_hook_active IS present on SubagentStop (docs say it isn't), and SubagentStop does fire for background subagents
Summary
Two measured contradictions between the hooks documentation and v2.1.220 behaviour, both affecting anyone writing a SubagentStop hook.
1. stop_hook_active IS present on SubagentStop
The hooks docs state, for stop_hook_active:
Not present on SubagentStop
Measured: it is present, and it is exactly the loop guard you'd want.
- first SubagentStop fire for a subagent: field absent/null
- every subsequent fire:
stop_hook_active: true
Captured by a SubagentStop hook that logs jq -c '{ev:.hook_event_name, sha:.stop_hook_active}':
{"ev":"SubagentStop","agent":"general-purpose","sha":null}
{"ev":"SubagentStop","agent":"general-purpose","sha":true}
{"ev":"SubagentStop","agent":"general-purpose","sha":true}
... (7 more, all true)
This matters because the docs actively steer hook authors away from the correct guard. Following them, you write a marker-file/TTL workaround for a field that already exists, and the documented alternative (decision-based control) is the thing that causes runaway loops.
2. SubagentStop fires for background/Agent-tool subagents
#82249 reports that SubagentStop does not fire for subagents launched via the Agent tool in async/background mode. On 2.1.220 it does — verified with run_in_background: true, 9 fires observed. That issue may be fixable/closeable, or the docs should state the current behaviour.
Repro
Minimal --settings file with a logging SubagentStop hook, then:
claude -p "Use the Agent tool to dispatch one general-purpose subagent whose entire task is to reply with the single word FOO. Then reply DONE." \
--settings ./probe-settings.json --model sonnet --effort high \
--allowedTools Agent --permission-mode dontAsk
Requested
- Correct the
stop_hook_activerow in the hooks reference, and show the guard idiom forSubagentStop([ "$STOP_ACTIVE" = "true" ] && exit 0). - Document that
hookSpecificOutput.additionalContextonSubagentStopre-invokes the subagent — the current wording ("non-error feedback that continues the conversation") does not make it obvious that "the conversation" is the subagent's, which makes it very easy to author an amplifying loop.
Environment
Claude Code 2.1.220, macOS 15 (Darwin 25.5.0), Sonnet 5 and Opus 5 both reproduce.