[BUG] SessionStart hooks incorrectly deduplicated across plugins
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?
When multiple plugins define SessionStart hooks with the same command pattern (e.g., python "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.py"), Claude Code incorrectly deduplicates them down to a single hook
execution.
Debug evidence shows: "Matched 1 unique hooks for query 'startup' (2 before deduplication)"
This means only ONE SessionStart hook runs, even though both plugins are enabled and both hooks are registered. The deduplication happens before ${CLAUDE_PLUGIN_ROOT} variable expansion, causing hooks from
different plugins with identical command strings to be treated as duplicates.
Expected: Both hooks should execute and their additionalContext values should be concatenated (per official documentation).
Actual: Only one hook executes, the other is silently dropped.
What Should Happen?
According to the official documentation at https://code.claude.com/docs/en/hooks#sessionstart-decision-control:
"Multiple hooks' additionalContext values are concatenated."
Expected behavior:
- Both SessionStart hooks should execute in parallel
- Each hook's hookSpecificOutput.additionalContext should be collected
- The outputs should be concatenated together
- Both plugin messages should appear at session startup
Example:
- ask-before-code hook outputs: "📋 ask-before-code Plugin Active..."
- quick-wins hook outputs: "🎯 quick-wins Plugin Active..."
- User should see BOTH messages concatenated at startup
The ${CLAUDE_PLUGIN_ROOT} variable should be expanded BEFORE deduplication checking, so that:
- "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.py" from ask-before-code
- "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.py" from quick-wins
Error Messages/Logs
No error messages shown to user, but debug logs reveal the issue:
Key debug log lines from claude --debug output:
Line 58: Loaded hooks from standard location for plugin ask-before-code: C:\Users\{username}\.claude\plugins\cache\flowaccount-dev-tools\ask-before-code\0.3.2\hooks\hooks.json
Line 76: Loaded hooks from standard location for plugin quick-wins: C:\Users\{username}\.claude\plugins\cache\flowaccount-dev-tools\quick-wins\0.3.2\hooks\hooks.json
Line 84: Registered 3 hooks from 7 plugins
Line 134-136 (THE SMOKING GUN):
Getting matching hook commands for SessionStart with query: startup
Found 2 hook matchers in settings
Matched 1 unique hooks for query "startup" (2 before deduplication)
Line 169-171: Only quick-wins hook output appears:
Hooks: Checking initial response for async: {"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": "🎯 **quick-wins Plugin Active**...
Result: ask-before-code hook never executes, no error message shown.
Steps to Reproduce
Reproduction repository: https://github.com/AndaAndaman/claude-plugins
Setup:
- Clone the repository and checkout the bug version:
```bash
git clone https://github.com/AndaAndaman/claude-plugins
cd claude-plugins
git checkout v0.3.2
- At v0.3.2, both plugins have identical SessionStart hook commands:
- ask-before-code/hooks/hooks.json:
{
"SessionStart": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "python \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start.py\"",
"timeout": 5
}]
}]
}
- quick-wins/hooks/hooks.json:
{
"SessionStart": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "python \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start.py\"",
"timeout": 5
}]
}]
}
- Install both plugins (add to marketplace or enable in settings)
- Start Claude Code with debug logging:
claude --debug 2>&1 | tee debug.log
Observe the bug:
- Check debug.log around line 134-136:
Getting matching hook commands for SessionStart with query: startup
Found 2 hook matchers in settings
Matched 1 unique hooks for query "startup" (2 before deduplication)
- Only ONE SessionStart message appears at startup (either ask-before-code OR quick-wins, not both)
Expected: Both messages concatenated
Actual: Only one message, other hook silently dropped
Workaround: v0.3.3 uses unique script names (session-start-ask-before-code.py, session-start-quick-wins.py)
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.23
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
PowerShell
Additional Information
## Root Cause Analysis
The deduplication logic compares raw command strings BEFORE variable expansion:
Before expansion (treated as duplicates):
- ask-before-code:
python "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.py" - quick-wins:
python "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.py"
After expansion (actually different files):
- ask-before-code:
C:\Users\{user}\.claude\plugins\cache\flowaccount-dev-tools\ask-before-code\0.3.2\hooks\session-start.py - quick-wins:
C:\Users\{user}\.claude\plugins\cache\flowaccount-dev-tools\quick-wins\0.3.2\hooks\session-start.py
## Impact
- Plugin developers cannot use standard naming conventions (e.g.,
session-start.py) - Documentation claims "multiple hooks' additionalContext values are concatenated" but this doesn't work
- No error/warning shown to users when deduplication occurs
- Likely affects other hook types (Stop, PreToolUse, etc.) with same pattern
## Proposed Fixes
Option 1 (Preferred): Perform deduplication AFTER ${CLAUDE_PLUGIN_ROOT} and other variable expansions
Option 2: Include plugin context in uniqueness check (hooks from different plugins never considered duplicates)
Option 3: Document the limitation clearly and recommend unique script names per plugin
## Workaround Implementation
v0.3.3 uses unique script names per plugin:
session-start-ask-before-code.pysession-start-quick-wins.py
This prevents deduplication but shouldn't be necessary.
## Related Documentation
Official docs claim concatenation: https://code.claude.com/docs/en/hooks#sessionstart-decision-control
> "Multiple hooks' additionalContext values are concatenated."
## Repository Links
- Bug reproduction: https://github.com/AndaAndaman/claude-plugins/tree/v0.3.2
- Workaround: https://github.com/AndaAndaman/claude-plugins/tree/v0.3.3
- Full debug log available upon request
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗