Claude Codeの「API Error: 400」、原因を全部洗い出して自分用チートシートにした

Claude Codeの「API Error: 400」、原因を全部洗い出して自分用チートシートにした

Claude Codeで一度は遭遇する「API Error: 400」。公式のエラーリファレンスとGitHub Issueの実例を照らし合わせ、cache_control・tool_use不整合・拡張思考の設定ミスなど原因別の見分け方と対処法を整理した。

エンジニアのゆとです。

長めのセッションを回してる最中に急にAPI Error: 400が出て、リトライしても/compactしても同じ場所で刺さる——という経験、Claude Codeを毎日使ってると一度はぶつかる。429や529と違って「待てば直る」タイプじゃないのが厄介なところで、原因はだいたい「こっちが送ったリクエストの中身」にある。

厄介なのは、400の中にも性質が全然違う複数のパターンが混ざってることだ。公式のエラーリファレンスに載ってる行儀の良いものもあれば、GitHub Issueで報告が積み上がってる「セッションが壊れて自己修復しない」タイプの厄介なものもある。毎回同じ調べ方をしてる自分への備忘も兼ねて、原因別に整理しておく。

まず大前提。400は「サーバーの調子」じゃなく「送った中身」の問題

Claude Codeが止まる原因は大きく3種類あって、まずここを区別しないと的外れな対処をしてしまう。

ステータス正体対処の方向性
429レート制限やスペンド上限に触れた一時的な状態秒〜分待てば解消することが多い
529Anthropic側の全体的な高負荷自分側では何もできない、待つかステータスページ確認
400リクエストの形式・内容そのものが無効リクエストの中身を直さないと直らない

429と529については以前まとめた。

Claude Codeの429エラー(Rate Limit)、使用量制限との違いを公式ドキュメントで切り分けた
Claude Codeの429エラー(Rate Limit)、使用量制限との違いを公式ドキュメントで切り分けたClaude Codeで429エラーが出ても、それはプランの月内制限とは別物。使用量制限・APIレート制限・529オーバーロードの違いを公式ドキュメントで整理し、原因別の対処法とヘッダー活用法をまとめた。読む →

400が厄介なのは、待っても直らないという一点に尽きる。同じ入力を投げ続ける限り同じ400が返り続けるので、/compactでごまかそうとしても失敗した位置で止まったままになるケースが多い。

公式のエラーリファレンスにある「行儀の良い400」

Claude Codeの公式ドキュメントには、400 invalid_request_errorとして扱われるパターンが整理されている。まずここを一次情報として押さえておく。

code.claude.com
Error reference - Claude Code Docs Claude Codeが出す代表的なエラーメッセージと原因・対処法を公式がまとめたリファレンスページ。400系のメッセージ文言もほぼ全部ここに載っている。

大きく分けると、以下の6カテゴリに収まる。

  1. コンテキスト超過系 — Prompt is too long Context limit reached · /compact or /clear to continue など。モデルのコンテキストウィンドウを超えている状態
  2. リクエストサイズ超過系 — Request too large(Messages APIは32MB上限)、Image was too large、PDF too large
  3. スキーマ検証系 — Extra inputs are not permitted、カスタムツールのinput_schemaが無効なJSON Schemaになっている場合
  4. モデル関連系 — Model ... is not a recognized model id、Claude Opus is not available with the Claude Pro plan、組織ポリシーでバージョンが古すぎる場合など
  5. 拡張思考(Extended Thinking)の設定ミス系 — 次の見出しで詳しく扱う
  6. ポリシー・セーフガード系 — appears to violate our Usage Policy、サイバーセキュリティ関連の話題を検知した場合

このうち1と6は読んで字のごとくで対処しようがない(会話をやり直すか内容を変えるしかない)。実務で厄介なのは3・4・5で、しかも「なぜそれが起きたか」が公式リファレンスだけだとわかりにくい。ここから先は、それぞれ実際にどういう状況で踏むのかをGitHub Issueと突き合わせて見ていく。

拡張思考(thinking)まわりの400 — 4.7以降で地雷が増えた

公式リファレンスの中で一番見落とされがちなのが、拡張思考の設定に関する400だ。Claude 4.7以降でthinkingの仕様が変わったのに、古い設定や古いプロキシ経由のリクエストがそのまま残っているケースで発生する。

代表的なメッセージと原因は次の通り。

メッセージ原因
"thinking.type.enabled" is not supported for this modelClaude 4.7以降はenabled(固定予算の拡張思考)自体が廃止済み。adaptiveに切り替える必要がある
adaptive thinking is not supported on this model逆に4.5以前のモデルにadaptiveを送っている。こちらはenabled + budget_tokensが必要
"thinking.type.disabled" is not supported for this modelFable 5.1系などthinkingが常時オンのモデルにdisabledを送っている
tool_choice: type "tool" and "any" are not supported for this modelFable 5.1系は強制ツール呼び出し(tool_choiceのtool/any)を未サポート
This model does not support assistant message prefillClaude 4.6以降はassistantメッセージのprefillが廃止されている

これが厄介なのは、モデルを切り替えた瞬間に踏むという点だ。自分の設定は変えていないのに、/modelでモデルを切り替えたりオートモードが別モデルに倒れたりした途端、以前は通っていたthinkingの指定が「そのモデルでは無効」に化ける。エラーメッセージの先頭に該当ブロックの位置(messages.1.content.0のような形式)が出るので、まずそこでどの設定が引っかかっているかを特定するのが早い。

もう一つ、拡張思考のブロックを自分で編集・並べ替え・除外してから送り返すとthinking blocks cannot be modifiedという400になる。ツール呼び出しを挟んだターンでは、thinkingとredacted_thinkingの両方をそのまま(中身が空でも)送り返す必要がある——ここでフィルタ処理をかけていると自滅するので注意したい。

コミュニティでは「cache_control」が鉄板パターン

公式リファレンスにはあまり具体的に書かれていないが、日本語圏の技術ブログで最も報告例が多いのがcache_control cannot be set for empty text blocksという400だ。GitHub Issueだけでも似た報告が何本も上がっている。

  • messages.170.content.1.text: cache_control cannot be set for empty text blocks(Issue #61123)
  • 画像だけを貼り付けてキャプションなしで送った時に起きるパターン(Issue #55156・#60766)
  • 特定メッセージ位置で毎回同じ箇所が壊れるパターン(Issue #61822)

原因は、プロンプトキャッシュ用のcache_controlパラメータが、中身が空のテキストブロックに付いたまま送信されてしまうこと。人間がAPIを直接叩いていて起きるというより、Claude Code自体のセッション管理(圧縮・再開・並列サブエージェントの合流)の過程で、空ブロックが生成された上にキャッシュ境界のマーカーが乗ってしまうケースが多い。プロンプトキャッシュまわりは自分も以前ハマったことがあるので、あわせて読んでもらえると仕組みが掴みやすいと思う。

Claude CodeでMCPサーバーを繋ぐとプロンプトキャッシュが壊れる——公式docsとGitHub Issueで条件を確認した
Claude CodeでMCPサーバーを繋ぐとプロンプトキャッシュが壊れる——公式docsとGitHub Issueで条件を確認したClaude CodeでMCPサーバーの接続・切断がプロンプトキャッシュを無効化しコストが跳ね上がる問題を、公式docsとGitHub Issue #92033の実測データから条件別に整理した。読む →

このタイプの400は一度発生すると、同じセッションを再開する限り同じ位置で再発し続ける。/compactをかけても、圧縮処理自体がその壊れた履歴を読みに行くので失敗する。対処は基本的に「そのセッションを畳んで新しいセッションで出直す」一択に近い。どうしても履歴を残したい場合は、該当セッションのJSONL(~/.claude/projects/**/*.jsonl)を直接開いて、壊れたcontentブロックのcache_controlキーを手動で削るという力技もあるが、正直バックアップを取った上での最終手段だと思っておいたほうがいい。

「tool_use ids were found without tool_result」も定番の詰まり方

もう一つよく見るのが、ツール呼び出し(tool_use)とその結果(tool_result)の対応が崩れて起きる400だ。公式リファレンスでは「Tool use or thinking block mismatch」としてtool use concurrency issuesという表現でまとめられている項目にあたる。

起きやすい状況:

  • サブエージェントやバックグラウンドタスクが並行して走っていて、片方のtool_useに対応するtool_resultが履歴に反映される前に、別のリクエストが送られてしまう
  • セッションを/resumeした直後の最初のターンで、前回セッション終了時に未完了だったtool_useが宙に浮いている
  • /branchでセッションを分岐した際に、分岐元の未解決なツール呼び出しがそのまま引き継がれる

いずれも「途中で切れた会話をそのまま再開しようとする」という共通点がある。ツール呼び出し中にセッションを強制終了したり、ネットワーク切断で結果が返ってこないままセッションを保存したりすると仕込まれやすい。接続が途中で切れるタイプのトラブルは以前もまとめたので、症状が似ている場合はこちらも参考にしてほしい。

Claude Codeの「Connection closed mid-response」、実は応答が完了した後に出ていた——v2.1.222で直った誤表示バグと、紛らわしいバグ5兄弟の正体
Claude Codeの「Connection closed mid-response」、実は応答が完了した後に出ていた——v2.1.222で直った誤表示バグと、紛らわしいバグ5兄弟の正体Claude Codeで「Connection closed mid-response」と表示されるのに応答自体は完了しているケースをv2.1.222の修正内容から解説。同時期に直った紛らわしい表示バグ(ストリームタイムアウト誤発火・部分出力の破棄・ターミナルフリーズ・描画停止)を整理し、本当に落ちているかの見分け方と対処法をまとめた。読む →

「Prompt is too long」は400だが429・529と勘違いしやすい

コンテキスト超過系の400は、体感としては「制限に引っかかった」感じがするので、429(レート制限)や使用量上限と混同しやすい。実際には全くの別物で、これは以前1本まるごと使って整理した。

Claude Codeで「Prompt is too long」が出て詰んだ時の対処法——「usage limit reached」と誤表示される罠も含めて整理した
Claude Codeで「Prompt is too long」が出て詰んだ時の対処法——「usage limit reached」と誤表示される罠も含めて整理したClaude Codeの「Prompt is too long」エラーの原因と復旧手順を解説。200kトークン制限との違い、「usage limit reached」への誤表示問題、MCPツール肥大化などの原因別チェックリスト、GitHub Issue実例つきでまとめた。読む →

ここで言いたいのは、Prompt is too long系のメッセージが出た時点で「モデル・コンテキストの中身」の話だと即座に判断していい、ということだ。待っても解消しないし、プランをアップグレードしても解消しない。/compactか/clearで会話量そのものを減らす以外に手がない。

自分がどのパターンか3ステップで切り分ける

400が出たら、原因の推測より先に次の順番で確認する。

  1. エラーメッセージの文言をそのまま読む — cache_control・tool_use・thinking・Prompt is too longのどれかのキーワードが含まれていれば、上のどのパターンかはほぼ一発で絞れる
  2. 直前に何をしたかを思い出す — モデル切り替え直後なら拡張思考の設定ミス、サブエージェント//resume//branchの直後ならtool_use不整合、長時間セッションの途中ならcache_control系、を疑う順番にする
  3. 新しいセッションで同じ操作を再現してみる — 新規セッションで同じ手順を踏んでも即座に再現するなら設定起因(拡張思考・モデル指定)、再現しないなら古いセッション固有の履歴破損(cache_control・tool_use)と判断できる

この切り分けだけで、無駄に/compactを連打して時間を溶かす回数はかなり減る。

セッションが壊れて動かなくなった時の復旧手順

原因の見当がついたら、実際の復旧は以下の優先順位で試す。

  1. Claude Codeを最新バージョンに更新する(claude updateまたはnpm update -g @anthropic-ai/claude-code)。cache_control関連やtool_use関連の400は、シリアライズ処理側のバグとして修正パッチが入ることが多い
  2. 一度アプリ・ターミナルを再起動して新しいセッションを開始する。履歴破損系の400は、同じ壊れたセッションを開き続ける限り再発する
  3. どうしても直前の会話内容を活かしたい場合は、該当のJSONLをバックアップした上で、壊れたメッセージ位置(エラーメッセージにmessages.N.content.Mの形式で出る)周辺の該当キーを手動で削除し、そこから再開する
  4. .envやCI設定でANTHROPIC_BETA系のヘッダーを固定で渡している場合、モデル移行のタイミングで無効な値になっていないか見直す(invalid beta flag系の400はここが原因のことが多い)

再発を減らすための運用チェックリスト

  • 長時間セッションは定期的に/compactではなく/clearで区切りを入れ、履歴を肥大化・複雑化させすぎない
  • サブエージェントやバックグラウンドタスクを並列で走らせる場合、tool_useの結果を待たずに次の指示を割り込ませない
  • モデルを切り替えた直後は、拡張思考(thinking)まわりの設定を明示的に見直す。特にFable系モデルへの切り替え時はtool_choiceやthinking.typeの指定を再確認する
  • 画像だけを貼り付けてメッセージを送るクセがあるなら、簡単でいいので必ずキャプションを添える
  • claude updateを定期的に流し、既知のシリアライズ関連バグの修正を取りこぼさない

FAQ

Claude Codeの400エラーとは何ですか

「送ったリクエストの形式か内容が無効」というエラーで、公式にはinvalid_request_errorというタイプ名で分類されている。サーバー側の一時的な不調(429・529)とは別物で、待っていても自然には直らない。

400が出たらプランをアップグレードすれば直りますか

直らない。400はプランの使用量や課金ティアの話ではなく、リクエストの中身の問題。プランのアップグレードで解決するのは429(レート制限)側の話で、混同すると無駄な出費になる。

/compactを繰り返しても同じ400が出続けます

cache_controlやtool_useが絡む履歴破損型の400は、/compact自体がその壊れた履歴を読みに行くため失敗する。新しいセッションを開始するか、該当のJSONLを直接編集して壊れたブロックを取り除く必要がある。

まとめ

Claude Codeの400は「サーバーが悪いわけじゃなく、こっちが送った中身の問題」という一点さえ押さえておけば、あとはパターン当てゲームに近い。公式リファレンスに載っている行儀の良いもの(コンテキスト超過・拡張思考の設定ミス・モデル指定ミス)と、コミュニティで報告され続けている履歴破損型(cache_control・tool_use不整合)の2系統があることを知っておくと、エラーメッセージを見た瞬間に対処の方向性が決まる。待っても直らないタイプのエラーだからこそ、切り分けの手順を持っておいたほうが結局は早い。

なお、エラーメッセージに[cyber]や「safeguards flagged」という文言が混ざっている場合は、この記事のパターンとは別物だ。セキュリティ系の作業を止める専用のセーフガードなので、原因の切り分け方は

Claude Codeの[cyber]セーフガード誤検知、CVP承認済みでも直らない件を整理した
を見てほしい。

← 記事一覧に戻る