「API returned an empty or malformed response」の原因と対処
Claude Codeに出る「API returned an empty or malformed response (HTTP 200)」の原因は、多くの場合プロキシやゲートウェイの横取りです。読み方と切り分け手順をまとめます。
API returned an empty or malformed response (HTTP 200) は、Claude Codeが「成功のはずの応答」を受け取ったのに、中身がClaude APIのメッセージではなかったときに出るエラーです。ほとんどの場合、Claude CodeとAPIの間にいるプロキシ、LLMゲートウェイ、ネットワークのサインインページが、APIの代わりに返事をしています。
このエラーが出る条件
エラー文にHTTP 200と書かれているのに失敗扱いになるので、最初は戸惑います。出る条件は次の連鎖です。
エラーに至る4段階
- 1
ストリーミングで送る
Claude Codeがストリーミングでリクエストを送ります。
- 2
ストリームが途中で失敗する
使えるデータが届かないまま、あるいは途中で、ストリームが失敗します。
- 3
非ストリーミングで送り直す
同じリクエストを非ストリーミングで送り直します(非ストリーミング再試行)。
- 4
成功ステータスなのに本文が違う
送り直しは成功ステータスで返りますが、本文がClaude APIのメッセージではありません。ここで再試行はもう行われず、そのターンはこのエラーで終わります。
4段階目の「本文」の例は、HTMLのエラーページやサインインページ、空の本文、別形式のJSONです。
ストリーミングの失敗だけなら、非ストリーミングへの送り直しで通常は完了します。
なぜ自動で直らないのか
Claude Codeは一時的な失敗を、指数バックオフをはさんで最大10回まで再試行します。エラー画面が出た時点で、その失敗に当てはまる再試行はもう済んでいる、というのが公式の前提です。
このエラーは、その再試行をしない失敗の一覧に入っています。同じ一覧には、TLS証明書の検証失敗、Bedrockのストリームでコンテンツタイプが想定と違う場合、組織のポリシーチェックに拒否されたリクエストが並びます。Bedrockの項には「ゲートウェイやプロキシが応答を書き換えるなら、再送も同じように書き換えるから」という理由が添えられています。
この一覧からは、再送しても結果が変わりにくい失敗が外れていると読めます。プロキシがHTMLを返す環境なら、もう一度送っても同じHTMLが返ってくるでしょう。待っても直らないので、経路の側を見にいくことになります。
エラー文の後ろを読む
冒頭の一文のあとに、何が返ってきたかを示す情報が続きます。原因の切り分けは、ほぼここで決まります。
Response:句 — コンテンツタイプ、本文の種類(body is an HTML page、empty bodyなど)、サイズ(バイト)、Anthropicのリクエストidの有無。nginxやcloudflareのようなサーバー名、cf-rayやviaのような中継系ヘッダーがあれば、それも並ぶ- 失敗したストリーミングリクエストのidと、再試行のきっかけになった失敗の内容。ストリームが開いていた場合は、届いたイベント数と、失敗時点での無音時間も出る
Anthropic APIが返した応答なら、リクエストidが付くのが普通です。idが無く本文がHTMLなら、間にいる別のシステムが返したと見てよいでしょう。サーバー名だけでは決め手になりません(次の節で扱います)。
Response: 句の手がかり | 疑う相手 |
|---|---|
body is an HTML page でリクエストidなし | 疑う相手プロキシ、ゲートウェイ、サインインページ |
nginx cloudflare などのサーバー名、cf-ray via ヘッダー | 疑う相手経路上のリバースプロキシやCDN |
empty body | 疑う相手本文を捨てるゲートウェイ、途中で切るプロキシ |
| JSONだがClaude APIの形式ではない | 疑う相手別形式で返すゲートウェイ |
body is an event stream でリクエストidあり | 疑う相手経路とは限らない(次の節) |
この表は、公式が挙げた本文の種類とサーバー名・ヘッダーの手がかりを読み替えた目安で、機器を断定するものではありません。
リクエストidがあるのに同じエラーが出る報告
anthropics/claude-code のIssueには、プロキシを疑う手がかりが逆を向く報告が複数あります。#88451(v2.1.238)、#89232(v2.1.241)、#87842(v2.1.235)の Response: 句は、どれも次の形です。
Response: content-type event-stream, body is an event stream (the non-streaming
request was answered with a stream), 3935 bytes, request-id req_…, server
cloudflare, intermediary headers anthropic-* cf-cache-status cf-ray. This was
the non-streaming retry of streaming request (no Anthropic request-id), which
failed with: other; 0 stream events received.最初のストリーミング側はイベントが0件でリクエストidも無く、送り直した非ストリーミング側は、JSONでなくイベントストリームで返っています。こちらにはリクエストidと anthropic-* ヘッダーが付き、サーバー名の cloudflare も並びます。上の表では cloudflare を経路の手がかりに挙げましたが、この型ではリクエストid付きの応答にも出ています。
#88110(v2.1.236〜v2.1.237、Intel Mac)の報告者は、プロキシ、VPN、SSL検査、ファイアウォール、DNS、再ログイン、再起動、別ネットワークを除外したと書いています。一度出るとプロセスを再起動するまで続き、同じネットワークのApple Siliconでは出ないそうです。原因の仮説は報告者のもので、HTTP/2の接続上で失敗したストリームの応答が次の要求に混ざる、というものです。
この型のIssueには解決策が書かれておらず、#88451、#89232、#88110はオープンのままで、#87842はクローズ済み(not planned)です。ANTHROPIC_BASE_URL を設定していない環境でidが付いたイベントストリームが返るなら、経路の改修より先に、Response: 句をそのまま残して報告する材料になります。
切り分けと対処
経路を直接叩いて確かめる
ゲートウェイ経由なら、Claude Codeを介さずに同じ経路へ直接リクエストを送ります。公式のゲートウェイ接続ガイドにある1トークンのリクエストです。
curl -sS -w '\n%{http_code}\n' -X POST "$ANTHROPIC_BASE_URL/v1/messages" \
-H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model": "claude-sonnet-4-6", "max_tokens": 1, "messages": [{"role": "user", "content": "."}]}'{"id":"msg_ で始まり "content":[...] を含むJSONが返れば、ゲートウェイには届いていて認証も通っています。HTMLが返るなら、そのルートが非APIの応答を返している証拠です。キーを x-api-key ヘッダーで受けるゲートウェイなら、ヘッダーを置き換えます。
コマンド中の claude-sonnet-4-6 は公式の例に書かれたモデル名です。ゲートウェイが別のモデル名で公開しているなら差し替えます。ただし、モデル名が未知だというエラーが返っても、URLと認証は通っています。ゲートウェイは認証を済ませてからモデル名を弾くため、公式も、このテストのためにゲートウェイが扱うモデルを探す必要はないとしています。
返ってきた内容ごとの読み方は次のとおりです。
| curlの結果 | 分かること |
|---|---|
{"id":"msg_ で始まり "content":[...] を含むJSON | 分かることゲートウェイに届き、認証も通っている |
| 未知のモデルを示すエラー | 分かることURLと認証は有効。モデル名だけが合っていない |
401 | 分かること認証情報が拒否された。変数の選び違いを疑い、もう一方の変数に替える |
| HTMLの本文 | 分かることそのルートが非APIの応答を返している |
| すぐに失敗する(接続拒否、ホスト名を解決できない) | 分かること本記事のエラー以前に、アドレスかネットワーク経路の問題 |
401 になる理由は、ANTHROPIC_AUTH_TOKEN が Authorization: Bearer に、ANTHROPIC_API_KEY が x-api-key に入るという違いにあります。認証情報を別の変数に入れると、ゲートウェイが読まないヘッダーで届いてしまいます。
curlが通れば安心、とも言い切れません。ゲートウェイの前段にあるWAF(Webアプリケーションファイアウォール)が、Claude Codeのプロンプトに含まれるXML風のタグやソースコードをクロスサイトスクリプティングの検査ルールに引っかけ、短いcurlは通るのに実際のセッションだけ弾く事例が、ゲートウェイの案内に載っています。この事例で返るのは 403 とHTMLの本文で、本記事の HTTP 200 とは状況が違います。ただ、「curlの1トークンが通ること」と「実際の会話が通ること」は別だと知っておくと、切り分けで迷いません。
curl に -i を足すと応答ヘッダーも見えるので、Response: 句のコンテンツタイプやサーバー名と突き合わせやすくなります(公式のコマンドにはない追加です)。
原因別のやること
- ゲートウェイやプロキシが返している場合 — その経路が非APIの応答を返す理由を直す。ゲートウェイのトラブルシューティング表でも、原因は「ゲートウェイか中間プロキシが非APIの応答を返した、多くはHTMLのエラーページかログインページ」とされている
- ゲストWi-Fiなど、サインインページのあるネットワーク — ブラウザーでサインインを済ませてから再試行する
- 非ストリーミング経路だけがゲートウェイで壊れている場合 — 環境変数
CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK=1を設定する
最後の環境変数は、ストリーミング中に失敗したリクエストを、非ストリーミングへの切り替えでなく通常のリトライ経路に流します。
export CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK=1
claude例外が1つあります。ストリーミングのエンドポイント自体が 404 を返すときは、この変数を設定していても非ストリーミングにフォールバックします。
この変数はエラーを消す魔法ではなく、フォールバックという1つの経路を閉じるだけです。環境変数の説明には、プロキシやゲートウェイが原因でフォールバックがツールの二重実行を生むケースにも使えると書かれています。ゲートウェイの問題そのものは直らないので、ストリーミング側が正常に通るかは別に確かめる必要があります。
二重実行が気になる理由も、公式の別の記述から読み取れます。ストリームが応答の途中で切れたとき、Claude Codeは完了したテキストやツール呼び出しのあとであれば、リクエストを再実行せず、完了済みの内容を残してツール呼び出しの結果から続きます。同じツール呼び出しを2回実行しかねないからです。プロキシやゲートウェイが原因でフォールバックが同じ状況を作ると、環境変数の説明にある「ツールの二重実行」が起きます。ファイルの編集やコマンドの実行が重複したように見えるときは、このフォールバックを疑う手があります。
似たエラーとの違い
同じ「ストリーミングと非ストリーミング」の文脈に、別のエラーが3つあります。混同しやすいので並べます。
| エラー | 起きること | 再試行 |
|---|---|---|
| 本記事のエラー | 起きること非ストリーミングの送り直しがHTTP成功でも、本文がAPIメッセージでない | 再試行しない |
Streaming response ended before any complete data was received | 起きることストリームが使えるデータを1つも返さず終わり、非ストリーミングで送り直した(その警告) | 再試行非ストリーミングで送り直す |
Bedrock streaming response has an unexpected content-type | 起きることAmazon Bedrockのストリームが期待するコンテンツタイプで来ない | 再試行しない |
No response from API | 起きること最初のバイトが期限内に来ない | 再試行最大1回 |
Streaming response ended before any complete data was received は、対処の入口が本記事とつながっています。この警告が出た直後に送り直しが成功すれば、そこで終わります。送り直しが「成功ステータスだが本文がAPIメッセージでない」なら、本記事のエラーに進みます。この警告が出るのはインタラクティブセッションだけで、1セッションにつき1回です。v2.1.239より前は、警告を出さずに黙って非ストリーミングで再送していました。
非ストリーミングの送り直しは、本文が違うだけでなく時間切れでも失敗します。その再送回数は CLAUDE_CODE_NONSTREAMING_TIMEOUT_RETRIES で制限でき、0 なら最初のタイムアウトで失敗します(v2.1.285以降)。ローカルセッションでは1回の試行が300秒で切れ、API_TIMEOUT_MS を正の値で設定していればその値になります。Claudeの生成が長引いて切れる場合は、再送しても同じ結果になります。
No response from API は、応答ヘッダーが期限内に来ない場合の話で、本記事とは失敗の位置が違います。待ち時間の調整はClaude Codeの応答が止まったときのタイムアウト調整方法、同名エラーの詳細は「No Response From API」が繰り返し出る原因と対処にあります。
バージョンで変わった点
エラー文とその扱いは、v2.1.234とv2.1.271の2回のリリースで変わっています。
- v2.1.234より前 — メッセージは
intercepting the requestで終わり、Response:句も失敗リクエストの情報もなかった(v2.1.231の報告#86427に載っているのは、この旧メッセージだけです) - v2.1.271より前 — 有効なAPIメッセージを
text/plainのような非JSONのコンテンツタイプで返す応答も、このエラーで終わっていた。LLMゲートウェイの中には、非ストリーミングの応答にそのコンテンツタイプを使うものがある
つまり、claude update で新しいバージョンに上げたあとに消えるエラーがあります。ゲートウェイが text/plain で正しいメッセージを返している場合です。古いバージョンで、エラー文に手がかりが少ないときは、まず更新が近道になります。更新後も出るなら、本物の非API応答です。
切り分けの順序
切り分けの5段階
- 1
ゲートウェイを使っているか確かめる
Claude Code内で
/statusを開き、Anthropic base URLの行があるかを見ます。行が無ければ、ANTHROPIC_BASE_URLは効いていません。ゲートウェイ無しでこのエラーが出るなら、ネットワークのサインインページかプロキシを疑います。 - 2
Response句を読む
HTML、リクエストid、サーバー名、本文の種類を確認します。idが無くHTMLか空の本文なら、経路を疑って次へ進みます。idが付いたイベントストリームなら、ゲートウェイ無しの環境かどうかを1段目の結果と合わせて見ます。
- 3
ゲートウェイを直接叩く
ゲートウェイがあるなら、上のcurlで直接叩きます。直接でも同じ異常が返れば、原因はゲートウェイ側です。
- 4
フォールバックを閉じる
ゲートウェイの非ストリーミング経路だけが壊れているなら、フォールバックを無効にします。
- 5
接続そのものを疑う
経路の接続が怪しいならClaude Codeプロキシ設定やUnable to connect to APIの原因と対処の切り分けへ進みます。
ゲートウェイ側の設定や検証手順はClaude CodeをLLMゲートウェイに接続する方法にまとめています。
直す場所は手元とは限らない
HTMLでidが無ければ経路の管理者へ、ゲートウェイ無しでid付きのイベントストリームが返るなら Response: 句を添えて anthropics/claude-code のIssueへ、と持ち込み先が分かれます。