Stop hooks in Skills never fire
Summary
Stop hooks defined in SKILL.md files never fire, even though the syntax is correct and the documentation indicates they should work.
Environment
- Claude Code version: Latest (as of 2026-01-19)
- OS: macOS Darwin 24.6.0
- Shell: zsh
Steps to Reproduce
- Create a skill at
~/.claude/skills/test-stop-hook/SKILL.md:
---
name: test-stop-hook
description: Test skill to verify Stop hooks work
hooks:
Stop:
- hooks:
- type: command
command: "touch ${HOME}/tmp/stop-hook-fired.txt"
---
# Test Stop Hook
This skill tests whether Stop hooks fire.
- Create the tmp directory:
mkdir -p ~/tmp
- Invoke the skill:
/test-stop-hook
- Have Claude complete some work
- Check if the Stop hook fired:
ls ~/tmp/stop-hook-fired.txt
Expected Behavior
According to the documentation:
- "Stop: Runs when the main Claude Code agent has finished responding"
- Skills can "define hooks scoped to this Skill's lifecycle. Supports PreToolUse, PostToolUse, and Stop events."
The Stop hook should fire after Claude finishes responding, creating the marker file.
Actual Behavior
The Stop hook never fires. The marker file is never created, regardless of:
- How many responses Claude makes
- Whether other skills are invoked afterward
- Whether the conversation ends
- How long you wait
Verified Working
- PreToolUse hooks in the same skill DO work - I have a PreToolUse hook with
matcher: "Bash"that fires correctly - The script itself works - Running it manually succeeds
- Script is executable - Permissions are
-rwxr-xr-x
Detailed Investigation
SKILL.md Configuration (working PreToolUse, non-working Stop)
---
name: process-pr-comments
description: Process PR review comments
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "${HOME}/.claude/skills/process-pr-comments/scripts/fetch-pr-data.sh"
once: true
Stop:
- hooks:
- type: command
command: "${HOME}/.claude/skills/process-pr-comments/scripts/post-summary.sh"
---
Whitespace Verification
Verified exact indentation using sed 's/ /·/g':
hooks:
··PreToolUse:
····-·matcher:·"Bash"
······hooks:
········-·type:·command
··········command:·"..."
··Stop:
····-·hooks:
········-·type:·command
··········command:·"..."
Indentation is correct (2-space YAML).
Debug Logging
Added debug logging to the Stop script:
echo "[$(date)] Stop hook fired" >> ~/tmp/stop-hook-debug.log
After an entire conversation with multiple responses, the log file shows only manual executions, never automatic Stop hook invocations.
Tests Performed
| Test | Result |
|------|--------|
| PreToolUse hook in same skill | ✅ Works |
| Stop hook in same skill | ❌ Never fires |
| Manual script execution | ✅ Works |
| Script permissions | ✅ Executable |
| YAML syntax validation | ✅ Valid |
| Wait for conversation end | ❌ Still doesn't fire |
| Invoke different skill | ❌ Still doesn't fire |
Questions
- When exactly should Stop hooks fire for Skills? The documentation says "when the main Claude Code agent has finished responding" but this doesn't seem to apply to Skills.
- Is there a difference between Stop hooks in
settings.jsonvsSKILL.md?
- Is there an explicit action required to "end" a skill session and trigger the Stop hook?
- Should Stop hooks use
context: forkandSubagentStopinstead?
Workaround
Currently working around this by having the skill instructions explicitly tell Claude to run the stop script manually at the end of processing.
Impact
This bug makes it impossible to:
- Automatically post summaries after skill completion
- Clean up temporary files after skill execution
- Restore state (like git branches) after skill work
- Any automated post-processing that should happen when a skill finishes
References
- Hooks documentation: https://docs.anthropic.com/en/docs/claude-code/hooks
- Skills documentation: https://docs.anthropic.com/en/docs/claude-code/skills
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗