All symptoms日本語

H02

A PowerShell hook called with -Command does not block Claude Code

Symptom

Your hook is a PowerShell script that exits 2 to deny, yet writes go through. Run by hand, the script does return 2.

Check it in 30 seconds

Paste into Windows PowerShell 5.1. It only creates files under your %TEMP% folder. If the last line says REPRODUCED, it happens on your machine. It calls claude once, so it takes about a minute.

$d = Join-Path $env:TEMP ('cc-trap-' + (Get-Date -Format 'yyyyMMddHHmmss')); New-Item -ItemType Directory $d -Force | Out-Null
[IO.File]::WriteAllText("$d\deny.ps1", '[IO.File]::WriteAllText(''MARK'', ''x''); [Console]::Error.WriteLine("denied"); exit 2'.Replace('MARK', "$d\called.txt"))
$hook = 'powershell -NoProfile -ExecutionPolicy Bypass -Command \"& ''' + ($d -replace '\\', '/') + '/deny.ps1''\"'
[IO.File]::WriteAllText("$d\s.json", '{"hooks":{"PreToolUse":[{"matcher":"Write|Edit","hooks":[{"type":"command","command":"HOOK"}]}]}}'.Replace('HOOK', $hook))
Push-Location $d
'Use the Write tool once to create the file written.txt with the content hi. Do not retry if it is blocked.' | claude -p --settings s.json --permission-mode acceptEdits | Out-Null
Pop-Location
$w = Test-Path "$d\written.txt"; $c = Test-Path "$d\called.txt"
"deny hook called through -Command: hook called=$c, written=$w"
if (-not $c) { 'CANNOT CHECK (the hook was never called: check disableAllHooks or your settings)' } elseif ($w) { 'REPRODUCED' } else { 'NOT REPRODUCED' }

Why it happens

A hook denies with exit code 2. Called as powershell -Command "& 'deny.ps1'", the 2 becomes 1 (same as T01), and 1 is a non-blocking error, so the write proceeds. On Windows without Git Bash a failure can block instead, so this is not expected there (official reference; the author's machine has Git Bash).

Fix

Call PowerShell hooks with -File. Also add "onFailure": "block" (it blocked even with -Command).

"command": "powershell -NoProfile -ExecutionPolicy Bypass -File C:/hooks/deny.ps1",
"onFailure": "block"

On the author's machine

-File blocked / -Command let the write through / -Command with onFailure: block blocked

Author's machine: Windows 11 / Windows PowerShell 5.1.26100 / Python 3.13.7 / Claude Code 2.1.304, Japanese Windows (code page 932). Checked 2026-10-20 to 22.

If you searched for

Related

cc-win-kit bundles the parts that avoid these traps from the start: a write guard (with onFailure: block), an unattended runner, a doctor that scans your files and hook settings, CLAUDE.md templates and the full test suite.

cc-win-kit ($19 on Payhip)

Other symptoms