Preflight Checklist
What's Wrong?
When running Claude Code with --dangerously-skip-permissions, PreToolUse hooks fire but don't block command execution. The command executes immediately while the hook runs asynchronously in the background. By the time the hook returns its denial (exit code 2), the command has already completed.
In my case, a git commit command succeeded despite the PreToolUse hook returning exit code 2 to block it. The hook denial arrived 37 seconds after the command was issued, but the commit had already been created.
What Should Happen?
PreToolUse hooks should block command execution synchronously, regardless of whether --dangerously-skip-permissions is enabled. When a hook returns exit code 2, the command should NOT execute.
The bypass flag should skip permission prompts, not skip synchronous hook execution.
Error Messages/Logs
// 08:42:38 - Command issued
{"ts":"2026-01-26T08:42:38.987Z","content":[{"type":"tool_use","id":"toolu_01TbrVfyWTwbgAxyZzbzR3LF","name":"Bash","input":{"command":"git commit -m \"feat: add linting...\"}}]}
// 08:43:15 - Hook denial arrives (37 seconds later!)
{"ts":"2026-01-26T08:43:15.641Z","content":[{"type":"tool_result","content":"Hook PreToolUse:Bash denied this tool","is_error":true,"tool_use_id":"toolu_01TbrVfyWTwbgAxyZzbzR3LF"}]}
// 08:43:54 - But commit already exists in git
{"ts":"2026-01-26T08:43:54.392Z","content":[{"tool_use_id":"toolu_01VZ8X7xwGGEfTDwq7pbjtLL","type":"tool_result","content":"ae5aec4 feat: add linting infrastructure and strict mode\n..."}]}
Session statistics:
- Total hook denials: 9 (all with `is_error: true`)
- Commits that succeeded despite denials: 5
- All commits were pushed to remote
Steps to Reproduce
-
Create a PreToolUse hook that takes 10+ seconds to execute:
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "sleep 10 && exit 2",
"timeout": 60
}]
}]
}
}
-
Launch Claude Code with bypass permissions:
claude --dangerously-skip-permissions
-
Ask Claude to run any bash command:
-
Observe:
- Command executes immediately and outputs "hello world"
- 10 seconds later, Claude receives "Hook PreToolUse:Bash denied this tool"
- The denial arrives AFTER the command already completed
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
No response
Claude Code Version
2.1.19
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
Real-World Impact
My PreToolUse hook runs quality checks (shellcheck, ruff, mypy, pytest) before commits. This takes 30-40 seconds. In bypass mode, commits proceed immediately while checks run in the background. By the time checks fail and return exit code 2, the commit is already in git history.
Session Evidence
Full transcript available in:
~/.claude/projects/-Users-user-Desktop--DONE-2026-01-25-cron-health/c2a76979-a3fb-4060-b7dc-5ef75e35dc05.jsonl
Hook Configuration
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "\"$HOME\"/.claude/hooks/validate-code-before-commit/commit-quality-check.sh",
"timeout": 300
}]
}]
}
}
Root Cause Hypothesis
The --dangerously-skip-permissions flag appears to make the entire tool execution pipeline asynchronous, not just permission prompts. Hooks fire (we see them return denials), but the command doesn't wait for hook completion before executing.
Documentation Gap
The hooks documentation doesn't specify whether PreToolUse hooks are synchronously awaited in bypass mode. This behavior should be either fixed or documented prominently.
Preflight Checklist
What's Wrong?
When running Claude Code with
--dangerously-skip-permissions, PreToolUse hooks fire but don't block command execution. The command executes immediately while the hook runs asynchronously in the background. By the time the hook returns its denial (exit code 2), the command has already completed.In my case, a
git commitcommand succeeded despite the PreToolUse hook returning exit code 2 to block it. The hook denial arrived 37 seconds after the command was issued, but the commit had already been created.What Should Happen?
PreToolUse hooks should block command execution synchronously, regardless of whether
--dangerously-skip-permissionsis enabled. When a hook returns exit code 2, the command should NOT execute.The bypass flag should skip permission prompts, not skip synchronous hook execution.
Error Messages/Logs
// 08:42:38 - Command issued {"ts":"2026-01-26T08:42:38.987Z","content":[{"type":"tool_use","id":"toolu_01TbrVfyWTwbgAxyZzbzR3LF","name":"Bash","input":{"command":"git commit -m \"feat: add linting...\"}}]} // 08:43:15 - Hook denial arrives (37 seconds later!) {"ts":"2026-01-26T08:43:15.641Z","content":[{"type":"tool_result","content":"Hook PreToolUse:Bash denied this tool","is_error":true,"tool_use_id":"toolu_01TbrVfyWTwbgAxyZzbzR3LF"}]} // 08:43:54 - But commit already exists in git {"ts":"2026-01-26T08:43:54.392Z","content":[{"tool_use_id":"toolu_01VZ8X7xwGGEfTDwq7pbjtLL","type":"tool_result","content":"ae5aec4 feat: add linting infrastructure and strict mode\n..."}]} Session statistics: - Total hook denials: 9 (all with `is_error: true`) - Commits that succeeded despite denials: 5 - All commits were pushed to remoteSteps to Reproduce
Create a PreToolUse hook that takes 10+ seconds to execute:
{ "hooks": { "PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "sleep 10 && exit 2", "timeout": 60 }] }] } }Launch Claude Code with bypass permissions:
Ask Claude to run any bash command:
Observe:
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
No response
Claude Code Version
2.1.19
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
Real-World Impact
My PreToolUse hook runs quality checks (shellcheck, ruff, mypy, pytest) before commits. This takes 30-40 seconds. In bypass mode, commits proceed immediately while checks run in the background. By the time checks fail and return exit code 2, the commit is already in git history.
Session Evidence
Full transcript available in:
~/.claude/projects/-Users-user-Desktop--DONE-2026-01-25-cron-health/c2a76979-a3fb-4060-b7dc-5ef75e35dc05.jsonlHook Configuration
{ "hooks": { "PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "\"$HOME\"/.claude/hooks/validate-code-before-commit/commit-quality-check.sh", "timeout": 300 }] }] } }Root Cause Hypothesis
The
--dangerously-skip-permissionsflag appears to make the entire tool execution pipeline asynchronous, not just permission prompts. Hooks fire (we see them return denials), but the command doesn't wait for hook completion before executing.Documentation Gap
The hooks documentation doesn't specify whether PreToolUse hooks are synchronously awaited in bypass mode. This behavior should be either fixed or documented prominently.