Claude Media
CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACKで再送を止める

CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACKで再送を止める

ストリームが途中で落ちたときの非ストリーミング再送を止める環境変数の使い方です。止めた後のエラーの出方、404の例外、再送のタイムアウト回数との違いを書きます。

CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK=1は、ストリーミングのリクエストが途中で失敗したときに、同じ内容を非ストリーミングで送り直す動きを止める環境変数です。止めると、ストリーミングのエラーは通常のリトライ層に渡ります。プロキシやゲートウェイが原因で、フォールバックがツール呼び出しの二重実行を起こしているときに使う変数です。

先に結論を並べます。

  • 設定は1を入れるだけで、settings.jsonのenvにも書けます
  • 止めたあとは、ストリームが壊れたときのエラー文言が変わります
  • ストリーミングのエンドポイントが404を返すときだけは、設定していてもフォールバックします
  • 再送回数を絞るCLAUDE_CODE_NONSTREAMING_TIMEOUT_RETRIESとは、止めるものが別です

非ストリーミング再送はどんなときに起きるか

Claude Codeは通常、応答をストリーミングで受け取ります。そのストリームが完了データを1つも届けずに終わると、Claude Codeは同じリクエストをストリーミングなしで送り直し、ターンを完了させます。

この再送が起きたとき、対話セッションには次の警告が1回だけ出ます。表示は1セッションにつき1回で、対話セッションに限られます。v2.1.239より前は、警告なしで黙って再送していました。

Streaming response ended before any complete data was received. Retrying without streaming. If this keeps happening, check any proxy or gateway between Claude Code and your model provider.

この場合、同じ内容のリクエストが2回送られます。空のストリーミング試行と、非ストリーミングの再送です。原因は、ストリーミングの応答本文を途中で消費したり加工したりするプロキシやゲートウェイであることが多いとされています。

フォールバック自体は古くからある機能です。v2.1.83では、フォールバック時のトークン上限が21kから64kに、ローカルでのタイムアウトが120秒から300秒に引き上げられました。詳細はv2.1.83のリリースノートにあります。

再送が起きる範囲は思ったより狭い

どの失敗でも非ストリーミングに落ちるわけではありません。ストリームの進み具合で扱いが分かれます。

失敗が起きた時点Claude Codeの動き
Claudeの応答がまだ何も完了していないClaude Codeの動き再試行するか、非ストリーミングで送り直す
テキストかツール呼び出しを1つ完了、または開始したあとClaude Codeの動き再送しない。完了した分を残してターンを続ける
応答が完了したあとClaude Codeの動き完全な応答を残し、通常どおりターンを終える

2行目の扱いには理由があります。リクエストを再送すると、同じツール呼び出しを2回実行しかねないからです。Claude Codeはそれを避けるために、再送せず完了した分を残します。

この変数が狙うのは、1行目の「まだ何も完了していない」段階で起きる非ストリーミング再送です。そこをプロキシやゲートウェイが壊していると、フォールバックが二重実行の引き金になり得ます。

止める手順と置き場所

変数は1にします。シェルから渡す場合は次のとおりです。

export CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK=1
claude

チームで揃えたいときは、settings.jsonのenvに書きます。この場合、claudeをどう起動しても効きます。

{
  "env": {
    "CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK": "1"
  }
}

置き場所によって適用範囲が変わります。

ファイル適用範囲
~/.claude/settings.json適用範囲自分の全プロジェクト
.claude/settings.json適用範囲プロジェクトの全員(リポジトリにコミット)
.claude/settings.local.json適用範囲自分のこのプロジェクトだけ
管理設定適用範囲組織の全員

ゲートウェイの不具合が一部の人の環境だけで出るなら、まず自分のsettings.local.jsonで試すのが安全です。効果が確かめられてから、プロジェクトか管理設定に広げます。

止めると何が変わるか

エラーはリトライ層へ回る

公式の説明では、ストリーミングのエラーはリトライ層に伝わります。Claude Codeは一時的な失敗を、指数バックオフで最大10回まで再試行します。つまり、非ストリーミング再送という別経路が消え、通常のリトライだけが残ります。

リトライの上限はCLAUDE_CODE_MAX_RETRIESで変えられます。リトライ枯渇の見分け方はretry exhaustionの検知方法にまとめています。

壊れたストリームのエラー文言が変わる

ストリームのイベントが落ちたり、重複したり、壊れたりして、Claudeがまだテキストもツール呼び出しも始めていないとき、通常はフォールバックで救われます。変数でそれを止めると、ターンは次のエラーで終わります。

ストリームの状態フォールバックを止めたときのエラー
イベントが落ちたフォールバックを止めたときのエラーAPI Error: Content block not found
イベントが重複したフォールバックを止めたときのエラーAPI Error: Content block already closed
イベントが壊れたフォールバックを止めたときのエラーAPI Error: Stream event unreadable、またはパーサーの生のエラー

これらは、止める前なら画面に出なかったエラーです。設定した直後にこの文言が出るようになっても、変数が悪さをしているわけではありません。これまでフォールバックが隠していたストリームの不具合が見えるようになった、という変化です。

ただし、Claudeが思考を終えただけの段階で壊れた場合は、フォールバックではなくリクエストの再発行になります。再発行したストリームも同じように壊れると、Part of the response never arrived and no response was produced. Try again.かThe response stream was malformed and no response was produced. Try again.でターンが終わります。この経路はこの変数の影響を受けません。

非ストリーミングの応答が壊れていたときの扱い

フォールバックを残している環境で、非ストリーミングの再送が成功ステータスを返しながら本文がClaude APIのメッセージでないと、API returned an empty or malformed responseでターンが終わります。HTMLのエラーページや、ネットワークのサインインページが典型です。

このエラーの読み方と切り分けはAPI returned an empty or malformed responseの原因と対処に書いています。非ストリーミング経路だけがゲートウェイで壊れているなら、この変数でフォールバックごと止めるのが公式の対処の1つです。

例外: エンドポイントが404を返すとき

この変数を設定していても、ストリーミングのエンドポイント自体が404を返すときは、Claude Codeは非ストリーミングにフォールバックします。つまり、ストリーミング非対応のゲートウェイを相手にしているなら、変数を入れても非ストリーミングで動き続けます。

この例外はAPI returned an empty or malformed responseの説明に書かれています。変数のリファレンス行には出てこないので、ゲートウェイを切り分けるときは見落としやすい点です。

Bedrockではどう関係するか

Amazon Bedrockの前に置いたゲートウェイがContent-Typeヘッダーを落とし、しかもストリームをサーバー送信イベントとして出し直す場合、Claude Codeは本文をデコードできません。このときは毎ターン、遅い非ストリーミング経路に落ちます。応答は完成してから一括で表示されます。

この症状への公式の対処は、CLAUDE_CODE_DISABLE_BEDROCK_CONTENT_TYPE_DEFAULT=1を設定して本文をサーバー送信イベントとして読ませることです。CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACKではありません。Bedrock側のヘッダーの問題は、Bedrockのcontent-typeエラーの原因と対処で扱っています。

フォールバックを止めても根本は直らず、ストリームの失敗が表に出るだけです。ゲートウェイが応答本文とヘッダーを加工せずに通すよう設定するのが、公式の求める直し方です。

再送のタイムアウト回数との違い

名前が似た変数にCLAUDE_CODE_NONSTREAMING_TIMEOUT_RETRIESがあります。v2.1.285以降で使え、タイムアウトした非ストリーミングリクエストの再送回数に上限を付けます。0にすると、最初のタイムアウトで失敗します。

変数止めるものフォールバックの有無
CLAUDE_CODE_DISABLE_NONSTREAMING_FALLBACK止めるもの非ストリーミング再送そのものフォールバックの有無止める(404は例外)
CLAUDE_CODE_NONSTREAMING_TIMEOUT_RETRIES止めるものタイムアウトした再送の回数フォールバックの有無残す

後者が効くのは、Claudeの応答が長く、非ストリーミングの1回の試行が時間切れになる場面です。生成に時間がかかる応答は、再送しても同じ結果になります。そのため、低い値にすれば早く失敗させられます。非ストリーミングの1回の試行は、ローカルセッションでは300秒で切れます。API_TIMEOUT_MSを正の値で設定していれば、その値が使われます。

使い分けは単純です。ゲートウェイがフォールバックを壊す(二重実行など)ならDISABLEを、フォールバックは生かしつつ長い待ちを避けたいならTIMEOUT_RETRIESを選びます。両方を同時に設定したときの挙動は、公式の説明には記載がありません。フォールバックを止めるなら再送は発生しないので、後者は要らなくなる、というのが自然な読みです。

二重実行を自分の環境で確かめる

ゲートウェイ経由で二重実行が起きているかは、ツール呼び出しを記録すれば確かめられます。次はPreToolUseフックで、ツール名と入力を1行ずつ追記する例です。公式のサンプルではなく、確認用に組んだものです。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "jq -c '{n: .tool_name, i: .tool_input}' >> /tmp/tool-calls.log"
          }
        ]
      }
    ]
  }
}

同じ入力のツール呼び出しが連続して2行並ぶなら、重複の疑いがあります。ストリーミングの警告が出たタイミングと重なるかを見ると、フォールバックが原因かどうかを絞れます。変数を設定する前後で同じ作業を行い、重複が消えるかを比べてください。

どんなときに設定するか

設定してよい場面は限られます。

  • ゲートウェイ経由で、同じツール呼び出しが重複して実行される
  • API returned an empty or malformed responseが、非ストリーミング経路を通ったときだけ出る
  • 「Streaming response ended before any complete data was received」の警告が頻繁に出る

逆に、普通にAnthropic APIへ直接つないでいる環境では、フォールバックは一時的な不具合の救済として働きます。無条件に止める理由はありません。

警告が頻発するなら、フォールバックを止める前にゲートウェイが応答本文とヘッダーを加工していないかを見るのが先です。そこが直れば、変数を使わなくても警告は止まります。応答が途中で止まるタイムアウトの調整は、Claude Codeの応答が止まったときのタイムアウト調整で扱っています。

よくある質問

設定すると応答は遅くなりますか

フォールバックを止めると、非ストリーミングで送り直す分の待ちはなくなります。代わりに、失敗は通常のリトライ層で処理されます。体感の速さがどう変わるかは、公式に記載がありません。

設定したのにフォールバックが起きます

ストリーミングのエンドポイントが404を返していないか確認してください。その場合は設定にかかわらずフォールバックします。

v2.1.239より前でも使えますか

変数自体はv2.1.83のリリースノートに載っています。v2.1.239より前は、フォールバックの警告が出ず黙って再送していました。警告が出ない環境では、フォールバックの発生に気づきにくい点に注意してください。

この記事を共有:XはてブLinkedIn