Hooks docs: stop_hook_active IS present on SubagentStop (docs say it isn't), and SubagentStop does fire for background subagents

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

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_active row in the hooks reference, and show the guard idiom for SubagentStop ([ "$STOP_ACTIVE" = "true" ] && exit 0).
  • Document that hookSpecificOutput.additionalContext on SubagentStop re-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.

View original on GitHub ↗