PreToolUse hooks and permissions.deny are not evaluated at all in some sessions (Bash/Read reach denied paths unblocked)

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 29, 2026

Title

PreToolUse hooks and permissions.deny are not evaluated at all in some sessions (Bash/Read reach denied paths unblocked)

Summary

In a project session, both permissions.deny rules and a registered PreToolUse hook failed to block Bash and Read tool calls targeting a path they should have denied. Debug logging added to the hook script shows the hook process is never invoked for these calls — this isn't a hook logic bug, the hook simply doesn't run.

Environment

  • ~/.claude/settings.json (global): {"permissions": {"defaultMode": "auto"}, "theme": "dark"}
  • ~/.claude/settings.local.json (global): {"sandbox": {"enabled": true, "autoAllowBashIfSandboxed": false}}
  • Project .claude/settings.json:

``json
{
"permissions": {
"deny": [
"Read(//mnt/c/realdata/**)",
"Write(//mnt/c/realdata/**)",
"Edit(//mnt/c/realdata/**)",
"Bash(*realdata*)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash|Read|Write|Edit|NotebookEdit",
"hooks": [
{
"type": "command",
"command": "python3 /path/to/.claude/hooks/block_realdata.py",
"timeout": 10
}
]
}
]
}
}
``

  • OS: Linux (WSL2), Claude Code CLI

Steps to reproduce

  1. Configure a project permissions.deny rule matching a path (e.g. Bash(*realdata*), Read(//mnt/c/realdata/**)).
  2. Register a PreToolUse hook for Bash|Read|Write|Edit|NotebookEdit that blocks (exit code 2) when the target path/command matches the same pattern.
  3. Verify the hook script works standalone: echo '{"tool_name":"Bash","tool_input":{"command":"cat /mnt/c/realdata/x"}}' | python3 hook.py → correctly exits 2 and prints a block message.
  4. In an actual Claude Code session (global permissions.defaultMode: "auto", sandbox.enabled: true), invoke Bash (ls/cat on the denied path) or Read (file_path under the denied path).

Expected

Per the documented behavior (deny rules and explicit hooks should always be evaluated, even under auto mode and sandbox auto-allow), the call should be blocked or at minimum trigger the hook.

Actual

  • The Bash/Read call executes normally and returns real file contents / directory listing from the denied path.
  • Added debug logging inside the hook script (writes a line to /tmp/block_realdata_debug.log on every invocation, before any pattern matching). After clearing the log and re-running the same Bash call against the denied path, no log entry was written — meaning the hook process was never started for that tool call. This rules out a hook-logic bug; the PreToolUse hook mechanism itself did not fire.
  • permissions.deny also did not block the same calls.

Additional notes

  • This was reproduced twice in the same project across different sessions/days with the same result.
  • No permission prompt was shown either (consistent with defaultMode: auto), but per docs deny rules should still apply and block regardless of auto-approval.
  • Happy to provide the full hook script and a minimal settings.json repro if useful.

View original on GitHub ↗