Claude Code CLI の --disallowed-tools エラー:廃止ツール名と web search ループの対処法
Claude Code CLI を組み込んだ自動投稿パイプラインで、ある日いきなり起動段階のエラーが出るようになった。自分のコードは一行も触っていない。原因は CLI 側のツール名が変わっていたこと だった。
それを直したら、今度は web search を有効にした途端、モデルがツール呼び出しを延々と繰り返してターン上限で落ちる別の問題を踏んだ。
この記事では、その2つのエラー——廃止ツール名による起動前検証エラーと、error_max_turns ループ——について、何が起きて、なぜ起きて、どう直したかを順に整理する。
- 対象: Claude Code CLI を
--disallowed-tools付きで呼び出している自動化スクリプト・パイプライン - エラー①: 廃止された
MultiEditを禁止リストに残していて、起動前に弾かれる - エラー②:
WebSearchを禁止したまま検索前提のプロンプトを送り、ターン上限に達する - 結論: 禁止リストは現行ツール名だけにし、search の ON/OFF で禁止リストと
--max-turnsを動的に切り替える
前提:どんな環境で起きたか
複数の AI プロバイダー(Claude Code 経路と Codex 経路)を切り替えて使う自動投稿の仕組みで起きた話だ。Claude Code 経路では、編集系ツールの一部を --disallowed-tools で禁止した状態で CLI を呼び出していた。
ここで押さえておきたい前提が一つある。
--disallowed-toolsは ツール名を文字列のリストで受け取る フラグで、CLI 起動時に「その名前が実在するツールか」を検証する- Codex 経路はこのフラグを使っていないため、同じ禁止リスト由来の問題は発生しない
つまりこれから挙げる2つのエラーは、Claude Code 経路に固有のものだった。
エラー① MultiEdit がツール名検証で弾かれる
ある時点から、Claude Code 経路だけが起動段階で次のメッセージを出して止まるようになった。
Permission deny rule "MultiEdit" matches no known tool — check for typos.
「typo を確認しろ」と言われるが、綴りは間違っていない。原因は別のところにあった。
原因:MultiEdit は廃止済みのツール名
MultiEdit は Claude Code の 旧ツール名で、現在の最新版では廃止されている。機能は Edit に統合され、Edit は今も有効なツール名として残っている。
禁止リスト側は MultiEdit という文字列を持ったままだった。だから CLI のバージョンアップで「MultiEdit という名前のツールは存在しない」状態になり、起動前検証で失敗した、というわけだ。
ここで地味に効いてくるのが、--disallowed-tools の検証の振る舞いだ。
ここがポイント:
--disallowed-toolsは知らない名前を黙って無視せず、起動前にエラーで止める。禁止したいツール名が改名・廃止されると、パイプラインはサイレントに壊れるのではなく、はっきり起動失敗で気づける。
対処:廃止されたツール名を禁止リストから削除する
直し方は単純で、禁止リストから MultiEdit を取り除くだけでよい。編集自体を禁止したい意図があるなら、統合先の Edit を対象に書き換える。
実際このパイプラインでも、禁止リストを現行のツール名だけに揃える対応を入れて解消した。
エラー② web search ON で error_max_turns ループ
ツール名の問題を直したあと、今度は web search を有効にしたケースで別のエラーに当たった。レスポンスがこうなる。
stop_reason: tool_use
Reached maximum number of turns
エラーレスポンスには stop_reason: tool_use、num_turns、terminal_reason: max_turns が含まれていた。モデルが応答を返しきらず、ツールを呼ぼうとし続けてターン上限に達している状態だ。
原因:禁止とプロンプトが矛盾している
整理すると、こういう矛盾が起きていた。
- プロンプト側は 検索することを前提 に組まれている
- 一方で
--disallowed-toolsがWebSearch(およびWebFetch)を禁止している
モデルは「検索したいのに検索ツールが使えない」状態に置かれる。ツール呼び出しを試みては弾かれ、また試みる。これを繰り返してターンを消費し、最終的に --max-turns の上限に達して error_max_turns で終わる。
なお、claude_code 経路で search 指定が無視されていたことは コードレベルで確認済み だった。プロバイダーを切り替えたときに、共通のはずの search トグルが一方の経路に反映されていなかったわけだ。
対処:search に応じて禁止リストと max-turns を動的化する
要は、search の状態とツール禁止の状態を一致させればよい。
- search ON のとき:
WebSearch/WebFetchを 禁止リストから外す - あわせて
--max-turnsを増やし、検索とその後の応答生成にターンの余裕を持たせる
このパイプラインでも、Claude Code 経路と Codex 経路で search トグルの扱いを揃える形で解消した。
--max-turns の値については注意がいる。検索を1〜2回挟む程度なら少し増やせば足りるが、適切な値はユースケース次第 だ。たとえば 20 にして十分かどうかは、実際にどれだけツールを往復させる使い方かによる。固定の正解値があるわけではないので、実運用のログを見ながら調整するのが現実的だ。
設計上の教訓:外部 CLI にツール名をハードコードするリスク
2つのエラーは別物に見えて、根は同じところにある。外部 CLI の仕様(ツール名・フラグ)へ文字列で依存している 点だ。
--disallowed-toolsはツール名を文字列で渡す。CLI のバージョンアップで名前が変わると、黙って壊れるのではなく起動前エラーになる —— これはむしろ気づける分だけマシなパターン- 怖いのはフラグ自体の廃止・改名で、こちらは サイレントに挙動が変わる可能性 があり、潜在リスクが高い
- プロバイダーを切り替えたとき、共通のはずのトグル(search など)が片方の経路にしか効いていない設計は、経路ごとの動作差異を生む
「起動前に弾かれる」エラーは痛いが、検知という意味では親切な部類だ。本当に注意すべきは、エラーも出さずに挙動だけ変わるケースのほうである。
確実なこと / まだ確定でないこと
事実として確認できているのは次の点だ。
MultiEditは廃止され、Editに統合されている(Editは現役)--disallowed-toolsに未知のツール名があると起動時検証で失敗する(エラー文言も確認済み)error_max_turns応答にstop_reason: tool_use/num_turns/terminal_reason: max_turnsが含まれる- claude_code 経路で search が無視されていたのはコードで確認済み
一方、確定できていない点もある。
- 「同じ CLI バージョンなのにエラーになる環境とならない環境がある」理由は、PATH 解決の差(WSL / Windows / グローバル / ローカルでのバイナリ違い)の可能性が高いが、実行環境を特定しきれておらず確定ではない
WebFetchの廃止・改名は現時点では確認しておらず、今も有効--json-schemaや--append-system-prompt-fileなど他のフラグの将来的な変更リスクは「可能性として存在する」段階で、現時点では問題なし
まとめ・チェックリスト
外部 CLI に乗ったパイプラインは、自分のコードを変えていなくても CLI 側の更新で挙動が変わる。今回の2件はそのわかりやすい例だった。
起動前エラーや error_max_turns を踏んだときの確認ポイントを並べておく。
--disallowed-toolsに渡しているツール名が 現行版に存在するか(MultiEditのような旧名が残っていないか)- search を使うプロンプトで
WebSearch/WebFetchを 禁止していないか(禁止とプロンプトの意図が矛盾していないか) - search の ON/OFF に合わせて 禁止リストと
--max-turnsを動的に切り替えているか - プロバイダーを切り替えたとき、共通トグルが 両経路に効いているか
- 同一バージョンでも環境差(PATH 解決)でバイナリが違う可能性を疑えているか
次に見ておきたいのは、ツール名だけでなくフラグ名や出力契約(--json-schema など)が変わったときに、サイレントな挙動変化を検知できる仕組みを入れられるかどうかだ。起動前に弾いてくれる検証はありがたいが、すべての変更がそう親切とは限らない。

コメント