Stop hook command type not dispatched from settings.json (plugin Stop hooks fire, settings.json ones don't)
Description
Command-type Stop hooks registered in ~/.claude/settings.json are never invoked by Claude Code, while plugin-registered Stop hooks (e.g., from claude-mem) fire reliably on every stop event.
Environment
- Claude Code version: v2.1.47 (claude-opus-4-6[1m])
- OS: Pop!_OS 24.04 LTS (Ubuntu-based)
- Terminal: Warp
Steps to Reproduce
- Register a command-type Stop hook in
~/.claude/settings.json:
{
"hooks": {
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "/path/to/stop-hook.sh"
}
]
}
]
}
}
- Create the hook script:
#!/usr/bin/env bash
echo "$(date -Iseconds) STOP FIRED" >> /tmp/stop-hook-test.log
exit 0
chmod +x /path/to/stop-hook.sh- Restart Claude Code (hooks are snapshot at startup)
- Have a normal conversation with tool usage
- Check
/tmp/stop-hook-test.log— file is never created
Expected Behavior
The Stop hook script should execute at the end of each assistant turn, similar to how plugin-registered Stop hooks fire.
Actual Behavior
The script is never invoked. Evidence:
- An unconditional debug log write at the top of the script never creates its log file
- A state file that should be consumed (deleted) by the hook persists indefinitely across turns and sessions
- Manual execution of the script (
echo '{}' | /path/to/stop-hook.sh) works correctly
Comparison with Plugin Hooks
Plugin Stop hooks (registered via hooks.json in plugin directories) do fire correctly. For example, claude-mem's Stop hooks (summarize and session-complete) execute on every stop event, confirmed via worker logs showing 40+ Stop events per day.
This suggests the Stop event is generated by Claude Code, but the dispatcher only routes it to plugin-registered hooks, not to settings.json command hooks.
Additional Context
- PreToolUse and PostToolUse command hooks registered in the same settings.json file work correctly
- The Stop hook registration schema matches the documented format and other working hook registrations
- Execute permissions are correct (rwxrwxr-x)
- The hook was registered before the session started (loaded in the startup snapshot)
Workaround
Moving suggestion delivery to PostToolUse hooks (which dispatch correctly from settings.json) as a workaround.
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗