Plugin hooks loaded but not included in hook registry lookups
Summary
Plugin hooks are successfully loaded at startup but are not found when the hook system queries for matching hooks. This causes plugin-defined hooks (like Stop hooks) to never execute.
Environment
- Claude Code version: 2.1.12
- OS: macOS (Darwin 23.5.0)
- Affected plugin: ralph-loop (from claude-plugins-official)
Expected Behavior
When a plugin with hooks is enabled, those hooks should be included in the hook lookup system and fire when their corresponding events occur.
Actual Behavior
Plugin hooks are registered at startup but never fire. The hook lookup system only checks "settings" hooks, not the merged plugin hooks.
Evidence from Debug Logs
At startup, the plugin hook is successfully loaded:
2026-01-17T23:29:33.186Z [DEBUG] Loaded hooks from standard location for plugin ralph-loop: /Users/[redacted]/.claude/plugins/cache/claude-plugins-official/ralph-loop/96276205880a/hooks/hooks.json
2026-01-17T23:29:33.215Z [DEBUG] Loading hooks from plugin: ralph-loop
2026-01-17T23:29:33.215Z [DEBUG] Registered 1 hooks from 1 plugins
But when hooks are queried, the registry shows 0 hooks:
2026-01-17T23:29:35.138Z [DEBUG] Getting matching hook commands for SessionStart with query: startup
2026-01-17T23:29:35.138Z [DEBUG] Found 0 hook matchers in settings
2026-01-17T23:29:35.138Z [DEBUG] Matched 0 unique hooks for query "startup" (0 before deduplication)
...
2026-01-17T23:32:19.287Z [DEBUG] Hooks: Found 0 total hooks in registry
The pattern continues for all hook events (PreToolUse, PostToolUse, Stop, etc.) - always "Found 0 hook matchers in settings" and "Found 0 total hooks in registry" despite 1 hook being registered at startup.
Impact
This bug completely breaks plugin-based hooks. Specifically for the ralph-loop plugin:
- Loop runs only once: The Stop hook that should intercept exit and restart the loop never fires, so Claude exits normally after one iteration.
- Loop starts in wrong session: The state file persists on disk. Due to timing/race conditions, a new session sometimes DOES have working hooks, finds the old state file, and starts looping in the wrong context.
Reproduction Steps
- Install and enable the ralph-loop plugin from claude-plugins-official
- Run
/ralph-loop "test task" --max-iterations 5 - Observe that Claude completes one iteration and exits instead of looping
- Check debug logs to see hooks registered but not found in registry
Configuration
The plugin is correctly enabled in settings:
{
"enabledPlugins": {
"ralph-loop@claude-plugins-official": true
}
}
The plugin's hooks/hooks.json is correctly formatted:
{
"description": "Ralph Loop plugin stop hook for self-referential loops",
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh"
}
]
}
]
}
}
Root Cause Hypothesis
Based on the debug logs, it appears plugin hooks are loaded into a separate registry from settings hooks, but the hook lookup functions only query the settings registry. The plugin hooks need to be merged into (or checked alongside) the main hook lookup system.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗