[BUG] Plugin Stop hooks never execute despite being registered (SessionStart works from same hooks.json) - Version 2.1.63
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
- Create a plugin with
hooks/hooks.jsoncontaining 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
}
]
}
]
}
}
stop.sh— minimal reproduction:
#!/bin/bash
echo "$(date) STOP FIRED" >> /tmp/plugin-stop-debug.log
exit 0
session-start.sh— same pattern:
#!/bin/bash
echo "$(date) START FIRED" >> /tmp/plugin-start-debug.log
exit 0
- Install plugin, start Claude Code session
- Verify
/pluginshows:Hooks: SessionStart,Stop(no errors) - Have Claude respond to any prompt
- Check logs:
/tmp/plugin-start-debug.log→ EXISTS (SessionStart fired)/tmp/plugin-stop-debug.log→ NOT FOUND (Stop never fired)
- Add to
.claude/settings.local.json:
{
"hooks": {
"Stop": [{ "hooks": [{ "type": "command", "command": "/absolute/path/to/stop.sh", "timeout": 15 }] }]
}
}
- Restart session, have Claude respond
/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.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗