The subagent has answered but not yet stopped
A subagent finishes responding, and before it stops, SubagentStop runs. Think of it as the subagent’s version of Stop: a check on the finished response and a way to send the agent back for more. The matcher filters the same built-in, custom, and plugin-scoped agent types as SubagentStart.
Its own transcript, and the parent’s background work
The payload carries permission_mode, stop_hook_active, agent_id, agent_type, agent_transcript_path, and last_assistant_message. The transcript path points at the subagent’s own record, separate from the parent session’s. background_tasks and session_crons arrive as well, and those two are scoped to the parent session. A hook can hold a subagent until a job the parent started has finished.
Keeping it working
Exit 0 lets the subagent stop unless a valid JSON decision or additionalContext continues it. Exit 1 lets it stop as well, and the transcript notes that the hook failed. Exit 2 prevents the stop and delivers stderr to the subagent as its next instruction. The JSON is the Stop shape: a top-level decision: "block" with a required reason, or hookSpecificOutput.additionalContext under hookEventName: "SubagentStop" for feedback not framed as an error. The hooks reference documents both under the Stop decision control.
The reviewer subagent skipped the tests directory, so your hook exits 2 with “the reviewer missed tests/” on stderr, expecting the parent to see it and redirect.
The parent sees nothing. The sentence goes to the subagent as its next instruction, and the subagent acts on it inside its own context. Written as a report to the parent, it reads to the subagent as a strange third-person note.
Write the stderr line as an instruction to the subagent: “review tests/ before you finish.” The report to the parent is what the subagent returns when it does stop.
Module 08 covers what a subagent returns to its parent, and module 09 covers the Stop shape this event reuses.