PreToolUse hooks not executing despite proper configuration
Bug Description
PreToolUse hooks are not executing when tools are called, despite being properly configured in ~/.claude/settings.json.
Environment
- Claude Code Version: Latest (as of 2025-12-18)
- OS: macOS (Darwin 25.1.0)
- Hook Type: PreToolUse (Bash matcher)
Evidence
1. Hook is properly configured
~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/Users/jheath/.claude/hooks/pre-tool-use-gh-issue-labels.sh"
}
]
}
]
}
}
2. Hook script works when tested manually
$ echo '{"tool_input":{"command":"gh issue create --repo excessivereason/claude-base --title \"Test\" --body \"Test\""}}' | ~/.claude/hooks/pre-tool-use-gh-issue-labels.sh
{
"decision": "block",
"reason": "GitHub issues must include at least one label. Add labels using --label flag.\n\n..."
}
✅ Hook correctly blocks the command when tested manually.
3. Hook does NOT fire when using Bash tool
When Claude uses the Bash tool to run gh issue create commands without labels:
- Issues are created successfully (should be blocked)
- No error message from hook appears
- Hook is completely bypassed
Test case: Created issues #250 and #252 in excessivereason/claude-base without labels - both went through despite hook being configured to block them.
Expected Behavior
When Claude attempts to run:
gh issue create --repo excessivereason/claude-base --title "Test" --body "Test"
The PreToolUse hook should:
- Execute before the Bash tool runs the command
- Detect missing labels
- Return
{"decision": "block", "reason": "..."} - Prevent the command from executing
Actual Behavior
The hook does not execute at all. Commands run without any validation, as if the hook doesn't exist.
Impact
This breaks critical validation workflows. In my case:
- User policy requires all GitHub issues to have labels
- Hook was created to enforce this automatically
- Without working hooks, issues are created without labels, violating policy
Reproduction Steps
- Create a PreToolUse hook in
~/.claude/hooks/that blocks certain Bash commands - Register it in
~/.claude/settings.jsonwith Bash matcher - Use Claude Code to run a Bash command that should be blocked
- Observe that the command executes without the hook firing
Additional Context
Other hook types (SessionStart, UserPromptSubmit, SessionEnd) appear to work correctly. The issue seems specific to PreToolUse hooks.
Related
- Hook script: User's custom validation hook for GitHub issues
- Use case: Enforce labeling requirements on issue creation
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗