Claude Code hook 在 Windows 上無法運作:原因與解法
同一個 hook 在 Mac 上執行正常,到了 Windows 卻毫無反應,甚至更糟,會擋下每一個指令。原因幾乎都出在 shell。以下說明 Claude Code 在 Windows 上如何執行 hook,以及如何讓你的 hook 穩定可靠。
簡答
在 Windows 上,Claude Code 會用 Git Bash 執行 hook 的 command;若未安裝 Git Bash,則改用 PowerShell。請用 "shell": "powershell" 或 "shell": "bash" 明確指定要用哪一個,讓 stdout 只輸出一個 JSON 物件(不要有設定檔的 echo),用結束代碼 2 來封鎖,並更新 Claude Code(多個 Windows hook 錯誤已在 2.1.310 以前的版本中陸續修正)。用 /hooks 和 claude --debug 來除錯。
哪個 shell 會執行你的 hook
Claude Code 會把 command 字串交給 shell:macOS 和 Linux 上是 sh -c,Windows 上是 Git Bash,若未安裝 Git Bash 則是 PowerShell。hook 參考文件中的這一句話,就能解釋大多數「在我的 Mac 上可以」的回報。為 PowerShell 撰寫的指令在 Git Bash 中會失敗,bash 指令碼在 PowerShell 中會失敗,而你拿到的是哪一個,取決於電腦上剛好有沒有安裝 Git for Windows。
用 shell 欄位消除猜測。它接受 "bash" 或 "powershell":
如果你想用 Git Bash 但 Claude Code 找不到它,請安裝 Git for Windows,或把 CLAUDE_CODE_GIT_BASH_PATH 設為它的位置,然後開啟新的工作階段。
症狀與成因
| 你看到的現象 | 可能原因 | 解法 |
|---|---|---|
| hook 似乎從未執行 | Claude Code 版本過舊、事件或 matcher 錯誤、設定中的 JSON 無效 | 更新;檢查 /hooks;移除註解和結尾逗號 |
ParserError 或「unexpected token」 | PowerShell 語法送到了 Git Bash,或反過來 | 明確設定 shell |
| hook 輸出「does not start with {」 | JSON 之前印出了其他內容,通常來自 shell 設定檔 | 讓設定檔不要輸出;把記錄寫到 stderr |
混用 \ 和 / 的路徑找不到 | Windows 路徑變數接上了 Unix 風格的後綴 | 使用 exec 形式:程式放在 command,每個引數放在 args |
Git Bash 回報 powershell: Permission denied | 在 2.1.283 和 2.1.304 至 2.1.305 中間歇性出現(#103910,尚未解決) | 使用 "shell": "powershell",避免中間經過 Git Bash |
| 防護 hook 讓所有東西都通過 | 結束代碼用了 1 而不是 2,或是舊版「失敗即放行」(fail-open)的錯誤 | 用結束代碼 2 封鎖;更新到 2.1.310 或更新版本 |
| hook 立即返回且沒有輸出 | shell 不會等待的 GUI 子系統程式 | 使用主控台程式,或等待該程序結束 |
stdout 只能有 JSON
Claude Code 會把 hook 的標準輸出當成它的回應。文件中的規則很簡短:stdout 除了 JSON 物件之外什麼都不要印。在 Windows 上,這條規則會以不易察覺的方式被打破:Git Bash 和某些環境仍會載入你的 shell 設定檔,而設定檔中無條件執行的 echo 會被加在 JSON 前面。結果 hook 在終端機中「正常」,在 Claude Code 裡卻失敗。
請改把記錄寫到檔案或 stderr,並讓設定檔只在互動式 shell 中才輸出。
結束代碼:2 會封鎖,1 不會
結束代碼 0 表示成功。結束代碼 2 是封鎖性錯誤:在可封鎖的事件上,Claude Code 會停止該動作並顯示你的 stderr。結束代碼 1 且沒有有效 JSON 時是非封鎖性錯誤,動作會繼續執行。 如果你的 hook 要強制執行某項政策,請用結束代碼 2。
並非每個事件都能這樣封鎖。Notification hook 的結束代碼會被忽略。以結束代碼 2 結束的 Stop hook 會讓 Claude 繼續工作。PermissionRequest hook 則完全忽略結束代碼 2,而是透過 JSON 中的 decision 物件做決定。
Claude Code 2.1.309 修正了 hook 的程式以代碼 2 結束時,PowerShell hook 不會封鎖的問題。如果你的 PowerShell 防護似乎被忽略,請先檢查版本。
值得為此更新的 Windows 修正
Anthropic 的變更記錄中有一長串 Windows hook 修正。以下是能解釋舊論壇討論串的幾項:
| 版本 | 修正 |
|---|---|
| 2.1.47 | PreToolUse 和 PostToolUse hook 在 Windows 上靜默失敗;現在改用 Git Bash 而非 cmd.exe 執行 |
| 2.1.161 | 明確呼叫 bash 的 hook(/usr/bin/bash script.sh)以「command not found」失敗 |
| 2.1.299 | 未設定 shell 的 PreToolUse 和 PermissionRequest hook,在沒有 Git for Windows 時,失敗會直接放行(fail open);現在失敗的防護會封鎖 |
| 2.1.309 | PowerShell hook 遇到結束代碼 2 時不會封鎖;"shell": "powershell" 防護在沒有 Git for Windows 時,失敗會直接放行(fail open) |
| 2.1.310 | 指令只有加引號字串(例如加引號的路徑)的防護,在沒有 Git Bash 時,失敗會直接放行(fail open) |
較早的 issue「No hook is working on Windows」(#10450)是在 2.0.27 上回報的;一位留言者追查到原因是 stdin 沒有傳到 hook 指令。如果你的版本這麼舊,請先更新再除錯其他東西。
直接呼叫程式
如果你的 hook 是編譯好的程式,可以用 exec 形式跳過 shell(args 欄位;設定 args 時會忽略 shell)。在 Windows 上,exec 形式要求 command 必須解析為真正的可執行檔,例如 .exe;.cmd 和 .bat 包裝檔(包括 npm 安裝的大多數東西)沒有 shell 就無法啟動。請指向 .exe,或使用 shell 形式。
我們在打造自己的 hook 輔助程式時學到一課:在 Windows 上,shell 啟動為 GUI 子系統建置的程式時不會等待它。shell 立即返回,代理程式讀到空白輸出,hook 看起來就像失效了。hook 程式必須是主控台程式。
外掛 hook 與路徑
建構 ${CLAUDE_PLUGIN_ROOT}/hooks/run.sh 這類路徑的外掛 hook,在 v2.1.2 到 v2.1.12 之間於 Windows 上失效(#18610):變數展開為反斜線路徑,再加上正斜線後綴,就成了 bash 無法解析的混合路徑。目前的文件提供了乾淨的解法:只要 hook 參照路徑預留位置,就使用 exec 形式。把程式放在 command,每個引數放在 args,例如 "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/format.js"]。每個元素都會作為單一引數傳遞,沒有 shell 會把它弄亂。
除錯檢查清單
/hooks會開啟唯讀清單,列出每個已設定的 hook 及其來源。claude --debug(或在工作階段中使用/debug)會顯示每次 hook 執行、其輸出和結束代碼。claude --debug-file <path>會把這些內容寫入檔案。- 驗證 JSON。 設定檔是嚴格的 JSON;一個結尾逗號就會讓整個檔案失效。
- 檢查 matcher。 matcher 區分大小寫;
"*"、""或不設 matcher 都會符合所有項目。 - 自己執行指令,使用 Claude Code 會用的同一個 shell,並以管道送入範例 JSON。
- 檢查逾時。 指令 hook 預設為 600 秒;需要等人回應的 hook 需要這樣的餘裕。
調查期間若要關閉所有 hook,請設定 "disableAllHooks": true。
如果你只是想要通知和核准
許多 Windows hook 設定只為了一個目的:知道 Claude Code 何時需要你,並回應它。Pet Island 不需要任何指令碼就能做到。它一鍵連接 Claude Code,替你處理 Windows 的細節,並把每個權限請求、提問和計畫,連同指令或差異內容放到螢幕頂端的一座小島上,讓你在那裡允許或拒絕。Claude Code 在 WSL 中執行時也能使用。
常見問題
在 Windows 上由哪個 shell 執行 Claude Code hook?
Git Bash;若未安裝 Git Bash 則是 PowerShell。在 hook 中設定 "shell": "powershell" 或 "shell": "bash" 即可明確選擇。
為什麼我的 Claude Code hook 在終端機中可以運作,在 Claude Code 中卻不行?
通常是多餘的輸出:會 echo 文字的 shell 設定檔被加到 hook 的 JSON 前面。stdout 只印出 JSON 物件即可。
如何讓 hook 封鎖工具呼叫?
以代碼 2 結束(或在 JSON 中回傳 deny 決定)。結束代碼 1 是非封鎖性錯誤,動作會繼續執行。PermissionRequest hook 會忽略結束代碼 2,改用 decision 物件。
hook 指令可以是 .cmd 或 .bat 檔案嗎?
exec 形式不行。在 Windows 上它必須解析為真正的可執行檔,例如 .exe。要執行 .cmd 或 .bat 檔案,請使用 shell 形式。
如何查看 hook 失敗的原因?
執行 claude --debug 或輸入 /debug,並用 /hooks 查看載入了哪些 hook。
資料來源
本頁所有設定與行為均已於 2026年10月26日 對照下列頁面確認。代理程式更新很快;若內容不再相符,請以官方頁面為準。
- Claude Code 文件:Hooks reference(Hooks 參考)
- Claude Code 文件:Hooks guide(Hooks 指南)
- Claude Code:CHANGELOG
- Issue #10450:Windows 上所有 hook 都無法運作
- Issue #18610:外掛 hook 無法在 Windows 上執行指令碼
- Issue #103910:hook 間歇性無法啟動 powershell(Git Bash)