Stop hooks in Skills never fire

Resolved 💬 4 comments Opened Jan 19, 2026 by SalesforceRocks Closed Feb 27, 2026

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

  1. 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.
  1. Create the tmp directory: mkdir -p ~/tmp
  1. Invoke the skill: /test-stop-hook
  1. Have Claude complete some work
  1. 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

  1. 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.
  1. Is there a difference between Stop hooks in settings.json vs SKILL.md?
  1. Is there an explicit action required to "end" a skill session and trigger the Stop hook?
  1. Should Stop hooks use context: fork and SubagentStop instead?

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

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗