Skip to content

[BUG] UserPromptSubmit hooks from plugins match but never execute #10225

Description

@severity1

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report
  • I am using the latest version of Claude Code

What's Wrong?

Summary: UserPromptSubmit hooks defined in plugin hooks.json files are registered and matched correctly but their commands never execute. The same hook configuration works when defined directly in ~/.claude/settings.json.

Environment:

  • Claude Code version: 2.0.24
  • OS: macOS (Darwin 24.6.0)
  • Settings location: /Users/jrpospos/.claude/settings.json

Steps to Reproduce:

  1. Create a plugin with UserPromptSubmit hook in hooks/hooks.json:
    {
      "hooks": {
        "UserPromptSubmit": [
          {
            "description": "Improves user prompts by adding clarifying questions",
            "hooks": [
              {
                "type": "command",
                "command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/improve-prompt.py",
                "description": "Run the prompt improvement script"
              }
            ]
          }
        ]
      }
    }
  2. Configure plugin in .claude-plugin/plugin.json pointing to the hooks file
  3. Enable the plugin in settings
  4. Start Claude Code and submit any user prompt
  5. Check debug logs with --debug flag

Expected Behavior

The hook command should execute when a user submits a prompt, similar to how Stop, PostToolUse, and SessionStart hook types work in plugins. Based on the hooks documentation, plugin hooks should "function identically to user/project hooks but integrate through a different mechanism."

Actual Behavior

  • The hook is registered correctly: [DEBUG] Registered 2 hooks from 1 plugins
  • The hook is matched correctly: [DEBUG] Matched 2 unique hooks for query "no match query"
  • The hook command never executes - no "Hook output" log line appears
  • No errors are logged

Comparison:

  • ✅ Manual ~/.claude/settings.json hook: Registers → Matches → Executes
  • ❌ Plugin hooks.json hook: Registers → Matches → Silent failure

Error Messages/Logs

Plugin installation (command never executes):

[DEBUG] Loading hooks from plugin: prompt-improver
[DEBUG] Registered 2 hooks from 1 plugins
[DEBUG] Getting matching hook commands for UserPromptSubmit with query: undefined
[DEBUG] Found 2 hook matchers in settings
[DEBUG] Matched 2 unique hooks for query "no match query" (2 before deduplication)

(No execution occurs - no output line)

Manual settings.json installation (works correctly):

[DEBUG] Getting matching hook commands for UserPromptSubmit with query: undefined
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 unique hooks for query "no match query" (1 before deduplication)
[DEBUG] Hook output does not start with {, treating as plain text

(Hook executes successfully)

Additional Context

  • The plugin hook structure follows the same format as working marketplace plugins (e.g., hook-auto-docs)
  • Other hook types (Stop, PostToolUse, SessionStart) work correctly in plugins
  • No marketplace plugins currently use UserPromptSubmit hooks, which may explain why this hasn't been caught
  • The ${CLAUDE_PLUGIN_ROOT} variable syntax is correct and matches working plugin examples
  • Complete debug logs available if needed

Workaround: Defining the same hook in ~/.claude/settings.json works as expected:

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.claude/hooks/improve-prompt.py"
          }
        ]
      }
    ]
  }
}

What Should Happen?

  1. UserPromptSubmit hooks defined in plugin hooks.json files should execute their commands after matching, consistent with other hook types
  2. The execution flow should be: Register → Match → Execute → Process output
  3. Plugin hooks and manual hooks should have identical execution behavior for all hook types

Metadata

Metadata

Assignees

Labels

duplicateThis issue or pull request already exists

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions