[BUG] SessionStart hooks never execute for local file-based marketplace plugins

Resolved 💬 8 comments Opened Nov 12, 2025 by kevincormier-toast Closed Mar 14, 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?

SessionStart hooks (and likely all hooks) never execute when plugins are loaded from a local file-based marketplace. The issue appears to be that hooks are only discovered in ~/.claude/plugins/*/hooks/hooks.json, but local file-based marketplace plugins are referenced in-place and never copied to that directory.

Key observations:

  1. Plugin commands and skills work correctly (proving the plugin is loaded)
  2. Hook execution shows success message: SessionStart:Callback hook success: Success
  3. But the hook script output is never captured or provided to Claude's context
  4. The /hooks command does not list hooks from local marketplace plugins

This differs from issue #10997 which affects GitHub marketplace plugins where hooks fail on first run but work after caching. With local file marketplaces, hooks never work because there's no caching/copying step.

Environment

  • Claude Code Version: 2.0.37
  • Platform: macOS (Darwin 24.6.0)
  • Installation: CLI
  • Marketplace Type: Local file-based (in-repo)

What Should Happen?

Local file-based marketplace plugins should have full feature parity with GitHub marketplace plugins, including hook execution. The hooks system should:

  1. Discover hooks from plugins regardless of installation location
  2. Resolve ${CLAUDE_PLUGIN_ROOT} correctly for in-place plugins
  3. Execute hooks and capture their output
  4. List hooks in /hooks output

Error Messages/Logs

Steps to Reproduce

  1. Create a test plugin with a SessionStart hook

Create this directory structure:
mkdir -p test-marketplace/test-plugin/{.claude-plugin,hooks,commands}

Create test-marketplace/.claude-plugin/marketplace.json:
{
"name": "test-marketplace",
"owner": {
"name": "Test",
"email": "[email protected]"
},
"metadata": {
"description": "Test marketplace",
"version": "1.0.0",
"pluginRoot": "."
},
"plugins": [
{
"name": "test-plugin",
"source": "./test-plugin",
"description": "Test plugin with SessionStart hook",
"version": "1.0.0",
"author": {
"name": "Test",
"email": "[email protected]"
}
}
]
}

Create test-marketplace/test-plugin/.claude-plugin/plugin.json:
{
"name": "test-plugin",
"version": "1.0.0",
"description": "Test plugin",
"author": {
"name": "Test",
"email": "[email protected]"
}
}

Create test-marketplace/test-plugin/hooks/hooks.json:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/test-hook.sh"
}
]
}
]
}
}

Create test-marketplace/test-plugin/hooks/test-hook.sh:
#!/bin/bash

cat <<'EOF'
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "TEST HOOK EXECUTED: If you can see this message, the SessionStart hook is working."
}
}
EOF

Make the script executable:
chmod +x test-marketplace/test-plugin/hooks/test-hook.sh

Create a simple test command test-marketplace/test-plugin/commands/test.md:
---
name: test
description: Test command to verify plugin is loaded
---

Reply with "Test plugin is loaded and working!"

  1. Install the marketplace and plugin

cd test-marketplace
claude /plugin marketplace add .
claude /plugin install test-plugin@test-marketplace

  1. Verify plugin is loaded (commands work)

claude /test

Expected: Should see "Test plugin is loaded and working!"
Actual: ✅ This works - confirms plugin is loaded

  1. Test if SessionStart hook executes

Start a new Claude session:
claude

Then ask Claude: "Can you see any test hook message in your context?"

Expected: Claude should see the "TEST HOOK EXECUTED" message in its context
Actual: ❌ Claude has no such message in context

  1. Verify hook is not registered

In the Claude session, run:
/hooks

Expected: Should list the SessionStart hook from test-plugin
Actual: ❌ Hook is not listed

  1. Verify hook script works when run directly

bash test-marketplace/test-plugin/hooks/test-hook.sh

Expected: Should output valid JSON
Actual: ✅ Outputs valid JSON correctly

---
This shows that:

  • The plugin loads (commands work)
  • The hook script is valid (runs correctly when executed directly)
  • But the hook is never discovered or executed by Claude Code

Claude Model

Not sure / Multiple models

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.0.37

Platform

AWS Bedrock

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Related Issues

  • #10997 - SessionStart hooks don't execute on first run with GitHub marketplace plugins (different - affects only first run,

not all runs)

  • #9354 - ${CLAUDE_PLUGIN_ROOT} variable issues (related but different)
  • #10871 - Plugin-registered hooks executed twice (affects working plugin hooks)

Workaround

Temporarily register hooks globally in ~/.config/claude/hooks/hooks.json with absolute paths to the plugin scripts. However,
this defeats the purpose of plugin-based hooks and doesn't scale for distributed plugins.

Impact

This prevents developers from:

  • Building plugins with hooks in local development environments
  • Distributing plugins via file-based marketplaces with hook functionality
  • Testing hook behavior before publishing to GitHub marketplaces

View original on GitHub ↗

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