onFailure: "block" 跟在腳本裡 exit 2 有什麼不同?
exit 2 是 hook 腳本正常跑完之後主動宣告「這個動作不准」,前提是腳本真的啟動並執行到那一行。onFailure: "block" 處理的是腳本根本沒跑起來、逾時、或非預期結束這類「檢查者自己出事」的情況。
兩者互補:exit 2 負責表達判斷結果,onFailure 負責確保檢查者倒下時預設不是放行。
所有 hook 都應該加上 onFailure: "block" 嗎?
不應該。通知、記錄、格式化這類輔助 hook 壞掉時,繼續執行通常比整個卡住更好;fail-closed 會把這些小問題放大成工作中斷。
只對「漏放的代價高於誤擋」的閘門開啟,例如守住正式環境、金流指令與客戶資料的那幾個。
怎麼確認我的閘門 hook 真的會擋?
最直接的做法是做一個「故意壞掉」的測試:把腳本路徑改成不存在,或讓它睡過 timeout,再觸發被它守住的動作,看動作是否真的被擋下。
另外可以用 claude Plugin validate 檢查 hook 的錯誤處理提示,但實測才是唯一能證明閘門在失敗時也守得住的方法。
這個選項在官方文件裡找得到嗎?我該不該現在就用?
我查閱時,官方 hooks 頁面可讀的部分還沒有收錄它,語意只來自 changelog 的一行說明。這代表欄位的精確位置與適用事件需要你自己驗證。
建議先在測試專案用故意失敗的 hook 確認行為,等文件補齊後再推到團隊共用設定。
Claude Code v2.1.295(2026 年 10 月 8 日)為 command 與 HTTP 類型的 hook 新增 onFailure: "block"。changelog 的描述是:當 hook 無法啟動、逾時或非預期結束時,直接擋下該動作。要理解它解決什麼,要先看以前的預設行為。
官方 hooks 文件寫得很清楚:對大多數事件,腳本路徑不存在或沒有執行權限時,shell 會以 127 之類的代碼結束,Claude Code 顯示 non-blocking 通知,動作繼續執行。文件甚至直接警告:設定政策型 hook 時,settings.json 裡打錯路徑會讓閘門「靜默失效」。逾時也一樣:PreToolUse 上逾時的 command、http 或 mcp_tool hook 不會擋住工具呼叫,文件說「不要指望卡住的 hook 當作閘門」。HTTP hook 的非 2xx 回應與連線失敗同樣只算非阻擋錯誤。就連 exit code 1 這個 Unix 慣例的失敗碼,在沒有有效 JSON 時也只是非阻擋,要擋必須用 exit 2。
換句話說,過去的政策 hook 是 fail-open:檢查器本身壞掉,等於沒有檢查。onFailure: "block" 讓你對單一 hook 選擇 fail-closed:它無法啟動、逾時或非預期結束,動作就被擋下,而不是繼續走正常權限流程。這對需要「寧可誤擋、不可漏放」的閘門特別重要,例如阻止寫入特定目錄、阻止部署指令、或呼叫外部合規 API 做核准的 hook。需要說明的是,我查到的官方 hooks 頁面(讀到的部分)還沒有收錄這個選項,上述語意來自 changelog 的一行描述;具體要放在哪個設定欄位、對哪些事件生效、與 timeout 的互動,請以官方文件更新後的說明為準,上線前自己用一個故意打錯路徑的 hook 驗證。
fail-closed 的代價是可用性:HTTP hook 依賴的合規服務一旦當機,所有被它守住的動作都會被擋。建議只對真正高風險的閘門開啟,並且在 hook 腳本裡自己處理可預期的錯誤;同時保留一個明確的逃生門,例如能快速停用該 hook 的 managed settings 開關。另外,v2.1.295 同時新增 claude Plugin validate 對 hook 的檢查提示,而 v2.1.290 已能列出每個閘門型 hook 是否有 .catch,兩者搭配可以在上線前找出沒有錯誤處理的閘門。
如果你的團隊用 hook 守住「不可碰的東西」(正式環境憑證、付款相關指令、客戶資料目錄),今天就盤點一次:哪些 hook 一旦壞掉會變成無聲放行?把這些列成清單,對最高風險的幾個加上 onFailure: "block",並寫一個故意失敗的測試。事後才發現閘門早就失效兩個月,付出的代價遠高於這十分鐘的檢查。