PreToolUse hooks declared in project .claude/settings.json never fire (user-level works)

Status Open
Reported on v2.1.257
Maintainer reply None cached
Activity 0 comments · opened Oct 6, 2026

Environment

  • Claude Code 2.1.257
  • Windows 11 (win32), Git Bash shell

Describe the bug

A PreToolUse hook with matcher "Bash" and an "if" condition, declared in
<project>/.claude/settings.json, never executes — even though the file is
valid JSON and matches the published settings schema. The identical hook
block, moved verbatim to ~/.claude/settings.json (user-level), fires
correctly and immediately in the same session, with no restart.

Steps to reproduce

  1. In a project, create .claude/hooks/test.sh that appends a line to a

sentinel file.

  1. Add to <project>/.claude/settings.json:

``json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/test.sh",
"if": "Bash(git *)"
}
]
}
]
}
}
``

  1. Validate the JSON (it's valid, matches schema).
  2. In the same Claude Code session, run any Bash tool call matching git *

(e.g. git log -1).

  1. Observe: the sentinel file is never written — the hook does not fire.
  2. Move the identical hooks block to ~/.claude/settings.json (user-level)

instead, repeat step 4.

  1. Observe: the hook fires immediately, sentinel is written.

Expected

A project-level PreToolUse hook should fire the same as a user-level one.

Actual

The project-level hook silently never registers/fires — no error, no
warning anywhere in the UI. Reproduced twice in one live session with two
separate trigger commands; user-level registration with the same config
fired on the very first trigger.

View original on GitHub ↗