claude-code - 💡(How to fix) Fix PreToolUse hook with Bash matcher silently not invoked (UserPromptSubmit + PostToolUse hooks fire fine) [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#52715Fetched 2026-04-24 10:41:36
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Participants
Timeline (top)
labeled ×4

A PreToolUse hook configured in ~/.claude/settings.json with matcher: "Bash" is not invoked on real Bash tool calls (including git commit), even though the same configuration's UserPromptSubmit and PostToolUse hooks fire correctly.

Manual invocation of the hook script with the exact same JSON payload the runtime would pass produces the expected behavior (block with exit 2). So the hook script is correct; the runtime's invocation is the gap.

Error Message

Only UserPromptSubmit and PostToolUse hooks fire. PreToolUse Bash hook is silently inert. No error message in any logs visible to the user.

Root Cause

A PreToolUse hook configured in ~/.claude/settings.json with matcher: "Bash" is not invoked on real Bash tool calls (including git commit), even though the same configuration's UserPromptSubmit and PostToolUse hooks fire correctly.

Manual invocation of the hook script with the exact same JSON payload the runtime would pass produces the expected behavior (block with exit 2). So the hook script is correct; the runtime's invocation is the gap.

Fix Action

Workaround

Manually invoke the gate skill before each commit. Awkward; defeats the purpose of an automated hook.

Code Example

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/pre-commit-gate.sh"
          }
        ]
      }
    ],
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/karpathy-reinjection.sh"
          }
        ]
      }
    ]
  }
}

---

{ printf '%s\n' "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] hook fired pid=$$ ppid=$PPID cwd=$PWD"; } >> /tmp/hook-debug.log 2>/dev/null || true

---

echo '{"tool_name":"Bash","tool_input":{"command":"cd /repo/path && git commit -m test"}}' | ~/.claude/hooks/pre-commit-gate.sh
   echo "exit=$?"
RAW_BUFFERClick to expand / collapse

Summary

A PreToolUse hook configured in ~/.claude/settings.json with matcher: "Bash" is not invoked on real Bash tool calls (including git commit), even though the same configuration's UserPromptSubmit and PostToolUse hooks fire correctly.

Manual invocation of the hook script with the exact same JSON payload the runtime would pass produces the expected behavior (block with exit 2). So the hook script is correct; the runtime's invocation is the gap.

Environment

  • Claude Code CLI (whichever version was current 2026-04-23/24 in this user's install — running claude --version would confirm)
  • macOS Darwin 24.6.0
  • Hook script at ~/.claude/hooks/pre-commit-gate.sh (executable, valid shebang #!/usr/bin/env bash)

Configuration

~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/pre-commit-gate.sh"
          }
        ]
      }
    ],
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/karpathy-reinjection.sh"
          }
        ]
      }
    ]
  }
}

A separate project-local .claude/settings.json defines a PostToolUse Bash hook (with if: "Bash(git push:*)" clause) that fires on every Bash tool call.

Reproduction

  1. Add a debug write to the top of ~/.claude/hooks/pre-commit-gate.sh:
    { printf '%s\n' "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] hook fired pid=$$ ppid=$PPID cwd=$PWD"; } >> /tmp/hook-debug.log 2>/dev/null || true
  2. Have Claude run a Bash tool call that contains git commit -m "test".
  3. Observe /tmp/hook-debug.log is never created — hook was not invoked.
  4. Manually pipe the equivalent payload into the hook:
    echo '{"tool_name":"Bash","tool_input":{"command":"cd /repo/path && git commit -m test"}}' | ~/.claude/hooks/pre-commit-gate.sh
    echo "exit=$?"
    → exit 2, prints "Staged changes have drifted from the last verified state" message — hook itself works correctly.

Expected

PreToolUse Bash hook should fire on every Bash tool call, mirroring how PostToolUse Bash hook fires.

Actual

Only UserPromptSubmit and PostToolUse hooks fire. PreToolUse Bash hook is silently inert. No error message in any logs visible to the user.

Impact

Skill-based commit gates (e.g., verify-before-commit skill that writes a per-repo marker the hook validates at commit time) are silently bypassed. Users believe their gate is enforced; in reality it isn't. CI catches the actual issues but the local pre-commit signal is lost.

Workaround

Manually invoke the gate skill before each commit. Awkward; defeats the purpose of an automated hook.

Notes

  • Tilde expansion (~/...) works for the other hook types (UserPromptSubmit script also uses ~).
  • Tested with both ~/... and absolute path for command: — neither caused the PreToolUse hook to fire.
  • The hook script returns exit 0 for non-git commit Bash calls (immediate pass-through), so even if it were firing on every Bash call, it would only block actual git commit attempts.

Asks

  1. Confirm whether PreToolUse + Bash matcher combination is supported in current Claude Code.
  2. If supported, identify the runtime path that's skipping invocation.
  3. If unsupported / deprecated, document the migration path (e.g., is Bash(git commit:*) the new matcher syntax?).

extent analysis

TL;DR

The PreToolUse hook with a Bash matcher may not be supported or correctly configured in the current Claude Code version, leading to its silent failure to invoke on Bash tool calls.

Guidance

  1. Verify Support: Check the Claude Code documentation or release notes to confirm if the PreToolUse hook with a Bash matcher is supported in the current version.
  2. Configuration Review: Ensure that the ~/.claude/settings.json configuration file is correctly formatted and that the PreToolUse hook is properly defined with the Bash matcher.
  3. Alternative Matcher Syntax: Investigate if an alternative matcher syntax, such as Bash(git commit:*), is required for the PreToolUse hook to work correctly.
  4. Debug Logging: Enable additional debug logging in Claude Code to see if there are any error messages or hints about why the PreToolUse hook is not being invoked.

Example

No code snippet is provided as the issue seems to be related to configuration or compatibility rather than code syntax.

Notes

The fact that UserPromptSubmit and PostToolUse hooks fire correctly, but PreToolUse does not, suggests a potential issue with the specific hook type or its configuration. The workaround of manually invoking the gate skill before each commit is cumbersome and defeats the purpose of automation.

Recommendation

Apply a workaround by manually invoking the gate skill before each commit until the root cause of the PreToolUse hook issue is identified and resolved. This ensures that skill-based commit gates are enforced, albeit manually, until a proper fix is implemented.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix PreToolUse hook with Bash matcher silently not invoked (UserPromptSubmit + PostToolUse hooks fire fine) [1 participants]