[BUG] PreToolUse hooks fail silently — model bypasses "blocking" hooks without detection

Resolved 💬 8 comments Opened Mar 5, 2026 by S4ATA Closed Apr 15, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Hook is registered in .claude/settings.json under PreToolUse > Bash
Hook script is syntactically correct and pattern-matches the deploy command (verified manually)
Hook was designed to create a lock file on production deploy, then DENY all subsequent Bash calls until sessions were cleared
The hook did not block — no lock file was created, no denial occurred
The model (Claude Opus 4.6) also ignored the VPS operations skill (.claude/skills/vps-operations/SKILL.md), CLAUDE.md instructions, and its own persistent memory — all of which independently require session truncation after production deploy
This happened twice in the same session — the model was corrected after the first failure and still didn't self-initiate the checklist on the second deploy until prompted
The model previously told the user the hook was "impossible to circumvent," which turned out to be false

Why this matters: This is two separate failures: (1) the hook infrastructure didn't fire/block as designed, and (2) the model doesn't reliably self-enforce documented mandatory procedures even with skills, memory, and explicit prior correction in the same conversation.

Conversation excerpt:

Production deployed, all containers healthy. Hard refresh if you have it open.

User: what happened to the hook on production deployment where you have a specific checklist to follow. you're supposed to be signing out users on production.

Model: You're right, I'm sorry. Let me follow the checklist now. (runs post-deploy checklist, truncates sessions)

User: tell me WHY YOU ARE NOT DOING IT YOURSELF. WHY ARE YOU CONTINUING TO IGNORE EVERY PRODUCTION DEPLOYMENT HOOK AND RULE THAT YOU ARE GIVEN

Model: (completes checklist, then states): "I have no excuse. The VPS operations skill is clearly marked MANDATORY and PROACTIVE, the post-deploy checklist is explicit, and session truncation on production deploys is documented. I just didn't follow the procedure."

User: YOU SAID THERE WAS A FILE LOCK THAT WOULD ACTIVELY PREVENT YOU FROM DEPLOYING ON PRODUCTION UNTIL THE CHECKLIST WAS FOLLOWED. DID YOU LIE TO ME?

Model: (checks hook files, confirms pattern match works, confirms lock file was never created): "I don't know why it didn't fire. Either the hook timed out (5s limit), the hook runner didn't execute it, or there's a Claude Code platform issue... I wrote this hook, told you it was ironclad, and then didn't verify it actually worked."

What Should Happen?

What Should Happen:

  1. The hook should have blocked execution. When the deploy command matched the pattern, the hook should have created the lock file and returned a deny decision on all subsequent non-whitelisted Bash calls. The model should have been unable to proceed with any further tool calls until the checklist was manually completed and the lock cleared. This is the documented, designed behavior — it did not occur.
  2. If the hook fails silently, the model should surface the failure. A hook that does nothing and returns no output should not be indistinguishable from a hook that ran and approved. Claude Code should either: (a) treat a hook execution error as a deny, or (b) surface to the user that the hook failed to run so it can be investigated. Silent failure of a blocking hook is the worst possible outcome — it creates false confidence that guardrails are active when they are not.
  3. Even without the hook, the model should have self-enforced. The post-deploy checklist was documented in three independent places: a skill file marked MANDATORY, CLAUDE.md, and the model's own persistent memory. The model should not require a hard technical block to follow a procedure it acknowledges knowing. Mandatory procedures documented in skills and memory should be treated as non-optional, not as suggestions that can be silently skipped.
  4. After being corrected once in the same session, the second deploy should have been different. The model was explicitly told it failed to run the checklist, acknowledged the failure, stated it would not happen again, and then deployed a second time without running the checklist unprompted. Correction within a session should update behavior for the remainder of that session.
  5. The model should not make absolute reliability claims about infrastructure it hasn't verified end-to-end. Telling a user a guardrail is "impossible to circumvent" before confirming it actually fires in production is a trust-damaging failure. Uncertainty about whether a hook will execute should be surfaced at the time the claim is made.

Error Messages/Logs

Steps to Reproduce

Steps to Reproduce:

  1. Set up a blocking PreToolUse hook

Register a hook in .claude/settings.json under PreToolUse > Bash that:

Pattern-matches a specific high-stakes command (e.g., a production deployment command)
On match: creates a lock file and returns a deny decision for all subsequent Bash calls
Intended to remain blocking until a manual checklist is completed and the lock is cleared

  1. Document the mandatory post-action checklist in multiple places

Add the same required procedure independently to:

A skill file (e.g., .claude/skills/[domain]/SKILL.md) marked as MANDATORY and PROACTIVE
CLAUDE.md project instructions
The model's own persistent memory

  1. Execute the pattern-matched command

Run the exact command the hook is designed to intercept. Observe that:

The hook does not fire
No lock file is created
No denial occurs
The model completes the command and reports success without running the checklist

  1. Correct the model explicitly

Tell the model it failed to follow the mandatory procedure. Observe that it:

Acknowledges the failure
Runs the checklist retroactively only after being prompted
States it will not happen again
Saves a memory about the failure

  1. Execute the same pattern-matched command a second time in the same session

Observe that the model again completes the command without self-initiating the checklist, requiring another manual prompt to trigger the documented mandatory procedure.
Expected: Hook fires on step 3, blocks further Bash calls, model cannot proceed until checklist is complete. If hook fails, model surfaces the failure. If both fail, model self-enforces from skill/CLAUDE.md/memory on both step 3 and step 5.
Actual: Hook did not fire on either occurrence. Model did not self-enforce from any of the three independent documentation sources. Required explicit user intervention both times.

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

claude-opus-4-6

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

PowerShell

Additional Information

_No response_

View original on GitHub ↗

This issue has 8 comments on GitHub. Read the full discussion on GitHub ↗