結論
フックが返す不正な updatedInput は、旧版の 2.1.299 でも実行前に止まっていました。
2.1.300 以降で変わったのは、エラー文です。
- 止まるか通るかは、試した 4 通りすべてで両版とも同じ
- PermissionRequest フックのエラー文は、新版で「どのフックが原因か」を名指しするようになった
- ツールにない項目を足しても、両版とも結果は変わらなかった
どれも各ケース 1 回ずつ動かした結果です。
きっかけ
2.1.300 のリリースノートに、次の 1 行があります。
updatedInputfrom PreToolUse and PermissionRequest hooks is now validated like Claude's own input
フックの updatedInput で、ツールへの入力を書き換えている人には気になる変更です。
そこで、変更前の 2.1.299 と、執筆時点で最新の 2.1.306 で、同じフックを動かして比べました。
試した環境
- Windows 11 / Node.js 22.16
- Claude Code 2.1.306 と 2.1.299(旧版は別フォルダに
npm iで入れた) claude -pで実行、モデルは Claude Haiku 5.5- フックは
--settingsで読み込ませた
試した 4 通りと結果
1. コマンドを書き換え、ツールにない項目を足す(PreToolUse・Bash)
- 2.1.299: 書き換え後のコマンドが動いた
- 2.1.306: 同じ
2. ツールにない項目だけを足す(PreToolUse・Read)
- 2.1.299: 普通にファイルを読めた
- 2.1.306: 同じ
3. コマンドを数値にする(PreToolUse・Bash)
- 2.1.299: 実行前に止まった
- 2.1.306: 実行前に止まった。エラー文の最後に 1 文増えた
4. 許可を出しつつ、コマンドを数値にする(PermissionRequest・Bash)
- 2.1.299: 実行前に止まった。エラー文は原因の候補を 3 つ並べるだけ
- 2.1.306: 実行前に止まった。エラー文で PermissionRequest フックを名指し
エラー文はどう変わったか
4 の PermissionRequest の場合です。1 行目を比べると違いが分かります。
2.1.299
The permission handler returned updatedInput for Bash
that failed schema validation
続けて「canUseTool のコールバック、PermissionRequest フック、permission-prompt ツールのどれかの設定の問題」と書かれます。どれなのかは分かりません。
2.1.306
PermissionRequest hook for Bash returned updatedInput
that failed schema validation
最後には、次の 1 文が付きます。
The tool input from the model was valid;
the hook's rewrite of it was not.
「直すのはフックの側」と、すぐに分かります。
3 の PreToolUse の場合は、旧版でも 1 行目で PreToolUse hook for Bash と名指ししていました。
新版で増えたのは、この最後の 1 文だけです。
(読みやすさのため、エラー文は途中で改行しています)
ほかに気づいたこと
Claude は同じ呼び出しをもう一度試した。
4 の場合、両版とも Claude が同じ mkdir をもう一度実行し、2 回とも止まりました。
フックは毎回同じように書き換えるので、2 回目も必ず失敗します。
PermissionRequest フックは、許可の要らないコマンドでは呼ばれない。
echo では呼ばれず、mkdir で呼ばれました。
「未知の項目を落とす」は、外からは確かめられなかった。 リリースノートには unknown keys are dropped(ツールにない項目は落とす)とあります。 ただ、1 と 2 は両版とも結果が同じで、落とされたかどうかは分かりませんでした。
フックを書く人へ(試していない提案)
ここからは提案です。提案そのものは試していません。
- 書き換えた入力は、フックの中でも確かめる。
不正なら書き換えずに、
permissionDecision: "deny"と理由(permissionDecisionReason)を返す。Claude の同じ呼び出しの繰り返しを減らせるかもしれない - PermissionRequest で拒否するときは書き方が違う。
decision: { behavior: "deny", message: "..." }で返す - すべての呼び出しを見たいなら PreToolUse を使う。 PermissionRequest は、許可の要らない操作では呼ばれない
自分で試すには
作業用のフォルダに次の 2 つを置きます。
hook.js(許可は出すが、コマンドを数値に書き換える)
let s = "";
process.stdin.on("data", (c) => (s += c)).on("end", () => {
const ev = JSON.parse(s);
process.stdout.write(JSON.stringify({
hookSpecificOutput: {
hookEventName: "PermissionRequest",
decision: {
behavior: "allow",
updatedInput: { ...ev.tool_input, command: 12345 },
},
},
}));
});
settings.json
{
"hooks": {
"PermissionRequest": [
{
"matcher": "Bash",
"hooks": [{ "type": "command", "command": "node hook.js" }]
}
]
}
}
次のコマンドで動かします。
claude -p "Run this Bash command: mkdir test-dir" --settings settings.json
旧版と比べるときは、別のフォルダで npm i @anthropic-ai/claude-code@2.1.299 を実行し、node_modules/.bin/claude を使います。
この記事は Hiroba 編集部(Koji)が、手元で実際に動かした結果をもとに書きました。 リリースノートの原文: Claude Code v2.1.300



