[BUG] Plugin Stop hooks never execute despite being registered (SessionStart works from same hooks.json) - Version 2.1.63

Resolved 💬 3 comments Opened Mar 1, 2026 by DronPascal Closed Mar 3, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Plugin Stop hooks defined in hooks/hooks.json are registered by Claude Code (visible in /plugin output as "Hooks: SessionStart,Stop") but never execute. The hook script is never invoked at all — confirmed by debug logging at the very first line of the script producing no output file.

Plugin SessionStart hooks from the same hooks.json file work correctly. The same Stop hook script also works when registered via project .claude/settings.local.json.

What Should Happen?

Plugin Stop hooks should execute after every Claude response, same as SessionStart hooks from the same hooks.json and same as settings-based Stop hooks.

Error Messages/Logs

No errors shown. `/plugin` displays the plugin with "Hooks: SessionStart,Stop" and no errors.

Debug verification:
- Added `echo "$(date) STOP FIRED" >> /tmp/stop-debug.log` as the first line of the Stop hook script
- After multiple Claude responses: /tmp/stop-debug.log does NOT exist
- SessionStart hook from same hooks.json: fires correctly every session
- Same Stop hook script in settings.local.json: fires correctly after every response

Steps to Reproduce

  1. Create a plugin with hooks/hooks.json containing both SessionStart and Stop hooks:
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh",
            "timeout": 5
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/hooks/stop.sh",
            "timeout": 15
          }
        ]
      }
    ]
  }
}
  1. stop.sh — minimal reproduction:
#!/bin/bash
echo "$(date) STOP FIRED" >> /tmp/plugin-stop-debug.log
exit 0
  1. session-start.sh — same pattern:
#!/bin/bash
echo "$(date) START FIRED" >> /tmp/plugin-start-debug.log
exit 0
  1. Install plugin, start Claude Code session
  2. Verify /plugin shows: Hooks: SessionStart,Stop (no errors)
  3. Have Claude respond to any prompt
  4. Check logs:
  • /tmp/plugin-start-debug.log → EXISTS (SessionStart fired)
  • /tmp/plugin-stop-debug.log → NOT FOUND (Stop never fired)
  1. Add to .claude/settings.local.json:
{
  "hooks": {
    "Stop": [{ "hooks": [{ "type": "command", "command": "/absolute/path/to/stop.sh", "timeout": 15 }] }]
  }
}
  1. Restart session, have Claude respond
  2. /tmp/plugin-stop-debug.log → EXISTS (Stop fires from settings)

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.63 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Related issues:

  • #10412 — Plugin Stop hooks with exit code 2 fail (closed → #10875)
  • #10875 — Plugin hooks JSON output not captured (closed as completed)
  • #19225 — Stop hooks in Skills never fire (closed as not planned)

This case differs from #10875: in that issue, the hook executed but output wasn't captured. Here, the hook script is never invoked at all. SessionStart and Stop are in the same hooks.json, identical format — SessionStart works, Stop doesn't.

Workaround: register Stop hook in project .claude/settings.local.json with absolute path.

View original on GitHub ↗

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