Skip to content

ストリーム中断/0 Token/署名無効:自己修復メカニズムとトラブルシューティングパス

Antigravity Tools で /v1/messages(Anthropic 互換)または Gemini ネイティブストリームインターフェースを呼び出すとき、「ストリーム出力が中断」「200 OK だが 0 Token」「Invalid `signature`」のような問題に遭遇した場合、このレッスンは UI からログへのトラブルシューティングパスを提供します。

学んだ後できること

  • 0 Token/中断問題がプロキシで通常「peek 先読み」でブロックされることを知っている
  • Proxy Monitor で今回のリクエストのアカウントとマッピングモデル(X-Account-Email / X-Mapped-Model)を確認できる
  • ログから「アップストリームストリーム早夭」「退避再試行」「アカウントローテーション」か「署名修復再試行」かを判断できる
  • どの状況ならプロキシの自己修復を待ち、どの状況なら手動介入すべきかを知っている

現在の悩み

以下の「現象」を見るかもしれませんが、どこから手を付けるかわからない:

  • ストリーム出力が途中で中断し、クライアントが「フリーズ」したように続きを続けない
  • 200 OK だが、usage.output_tokens=0 または内容が空
  • 400 エラーに Invalid \signature`Corrupted thought signaturemust be `thinking`` などが現れる

これらの問題の多くは「リクエストを間違えた」のではなく、ストリーム転送、アップストリームレート制限/変動、または履歴メッセージに含まれる署名ブロックがアップストリーム検証をトリガーしたことです。Antigravity Tools はプロキシ層で複数の防衛線を敷いており、あなたは固定パスに従って実際にどのステップで詰まっているかを検証するだけです。

0 Token とは?

0 Tokenは通常、一回のリクエストが最終的に返した output_tokens=0 で、かつ「コンテンツが生成されていないように見える」ことを指します。Antigravity Tools では、より一般的な原因は「ストリームレスポンスが本当に出力前に終了/エラーになった」ことで、モデルが本当に 0 個の token を生成したことではありません。プロキシは peek 先読みを使ってこの種の空レスポンスをブロックし、再試行をトリガーしようとします。

プロキシが背後で行う 3 つのこと(まず心のモデルを作る)

1) 非ストリームリクエストが自動的にストリームに変換される可能性がある

/v1/messages パスで、プロキシは内部的に「クライアント非ストリームリクエスト」をストリームリクエストに変換してアップストリームに要求し、SSE を受信した後、JSON に集約して返します(こうする理由はログに「better quota」と書いてあります)。

ソースコード証拠:src-tauri/src/proxy/handlers/claude.rs#L665-L913

2) Peek 先読み:まず「最初の有効データブロック」を待ってからストリームをクライアントに渡す

/v1/messages の SSE 出力に対し、プロキシはまず timeout + next() で先読みし、ハートビート/コメント行(: で始まるもの)をスキップして、最初の「空でなく、ハートビートでない」データブロックを取得してから正式に転送を開始します。peek 段階でエラー/タイムアウト/ストリーム終了が出ると、直接次回試行に入ります(次回は通常アカウントローテーションをトリガーします)。

ソースコード証拠:src-tauri/src/proxy/handlers/claude.rs#L812-L926;Gemini ネイティブストリームも同様の peek があります:src-tauri/src/proxy/handlers/gemini.rs#L117-L149

3) 統一退避再試行 + ステータスコードに基づいて「アカウントをローテーションするかどうか」を決定

プロキシは一般的なステータスコードに対して明確な退避戦略を行い、どのステータスコードがアカウントローテーションをトリガーするかを定義しました。

ソースコード証拠:src-tauri/src/proxy/handlers/claude.rs#L117-L236

🎒 始める前の準備

手順通りに進める

第 1 ステップ:どのインターフェースパスを呼び出しているかを確認

なぜか/v1/messages(claude handler)と Gemini ネイティブ(gemini handler)の自己修復の詳細は異なり、まずパスを確認すれば間違ったログキーワードで無駄な時間を費やすことを回避できます。

Proxy Monitor を開き、失敗したそのリクエストを見つけ、まず Path を記録:

  • /v1/messagessrc-tauri/src/proxy/handlers/claude.rs のロジックを見る
  • /v1beta/models/...:streamGenerateContentsrc-tauri/src/proxy/handlers/gemini.rs のロジックを見る

表示されるべきもの:リクエスト記録で URL/メソッド/ステータスコード(およびリクエスト所要時間)が見えます。

第 2 ステップ:レスポンスヘッダーから「アカウント + マッピングモデル」を把握

なぜか 同じリクエストの失敗/成功は、多くの場合「今回どのアカウントが選択されたか」「どのアップストリームモデルにルーティングされたか」に依存します。プロキシはこの 2 つの情報をレスポンスヘッダーに書き込み、先に記録しておけば、後でログを見た時に対照できます。

失敗したそのリクエストで、これらのレスポンスヘッダーを探します:

  • X-Account-Email
  • X-Mapped-Model

この 2 つは /v1/messages と Gemini handler で設定されます(例:/v1/messages の SSE レスポンス内:src-tauri/src/proxy/handlers/claude.rs#L887-L896;Gemini SSE:src-tauri/src/proxy/handlers/gemini.rs#L235-L245)。

表示されるべきものX-Account-Email はメールアドレス、X-Mapped-Model は実際にリクエストしたモデル名。

第 3 ステップ:app.log で「peek 段階で失敗」か判断

なぜか peek 失敗は通常「アップストリームが有効データを出力し始めなかった」ことを意味します。この種の問題の最も一般的な処理方法は再試行/アカウントローテーションで、プロキシがトリガーしたか確認する必要があります。

まずログファイルを特定(ログディレクトリはデータディレクトリの logs/ から、日ごとにロールして app.log* に書き込み)。

bash
ls -lt "$HOME/.antigravity_tools/logs" | head
powershell
Get-ChildItem -Force (Join-Path $HOME ".antigravity_tools\logs") | Sort-Object LastWriteTime -Descending | Select-Object -First 5

次に最新の app.log* でこれらのキーワードを検索:

  • /v1/messages(claude handler):Stream error during peek / Stream ended during peek / Timeout waiting for first datasrc-tauri/src/proxy/handlers/claude.rs#L828-L864
  • Gemini ネイティブストリーム:[Gemini] Empty first chunk received, retrying... / Stream error during peek / Stream ended immediatelysrc-tauri/src/proxy/handlers/gemini.rs#L117-L144

表示されるべきもの:peek 再試行がトリガーされると、ログに類似の「retrying...」警告が現れ、その後次回 attempt に入ります(通常アカウントローテーションをもたらします)。

第 4 ステップ:400/Invalid signature の場合、プロキシが「署名修復再試行」をしたかを確認

なぜか 署名類エラーは履歴メッセージの Thinking ブロック/署名ブロックがアップストリームの要件を満たしていないことから来ることが多いです。Antigravity Tools は「履歴 thinking ブロックのダウングレード + 修復プロンプト注入」で再試行を試みるので、まず自己修復を走らせる必要があります。

2 つの信号で修復ロジックに入ったかを判断できます:

  1. ログに Unexpected thinking signature error ... Retrying with all thinking blocks removed. が現れる(src-tauri/src/proxy/handlers/claude.rs#L999-L1025
  2. その後履歴 Thinking ブロックを Text に変換し、最後の user message に修復プロンプトを追加(src-tauri/src/proxy/handlers/claude.rs#L1027-L1102;Gemini handler も contents[].parts に同様のプロンプトを追加:src-tauri/src/proxy/handlers/gemini.rs#L300-L325

表示されるべきもの:プロキシは短い遅延後に自動再試行(FixedDelay)し、次回試行に入る可能性があります。

チェックポイント ✅

  • [ ] Proxy Monitor でリクエストパス(/v1/messages または Gemini ネイティブ)を確認できる
  • [ ] 今回のリクエストの X-Account-EmailX-Mapped-Model を取得できる
  • [ ] logs/app.log* で peek/再試行関連キーワードを検索できる
  • [ ] 400 署名エラーに遭遇したとき、プロキシが「修復プロンプト + thinking ブロッククリア」の再試行ロジックに入ったか確認できる

罠の警告

シナリオあなたがしそうなこと(❌)推奨做法(✓)
0 Token を見てすぐに何度も手動再試行クライアントの再試行ボタンを押し続け、ログを全く見ないまず一度 Proxy Monitor + app.log を見て、peek 段階で早夭しているか(自動再試行/ローテーション)を確認
Invalid \signature`` に遭遇したらすぐデータディレクトリをクリア.antigravity_tools 全部削除、アカウント/統計全部消えるまずプロキシに一度「署名修復再試行」を実行させる。ログが明確に回復不可と提示した時だけ、手動介入を考える
「サーバー側変動」を「アカウントが壊れた」と間違える400/503/529 全部アカウントローテーションローテーションが有効かどうかはステータスコードに依存する。プロキシ自体に should_rotate_account(...) ルールがある(src-tauri/src/proxy/handlers/claude.rs#L226-L236

レッスンまとめ

  • 0 Token/ストリーム中断はプロキシで通常先に peek 先読みを経る;peek 段階が失敗すると再試行をトリガーし次回 attempt に入る
  • /v1/messages は非ストリームリクエストを内部的にストリームに変換してから JSON に集約する可能性があり、これが「なぜストリーム問題に見えるのか」を理解するのに影響する
  • 署名無効類 400 エラーは、プロキシが「修復プロンプト + thinking ブロッククリア」で再試行を試みるので、優先的にこの自己修復パスが通っているかを検証する

次のレッスン予告

次のレッスンでは エンドポイントクイックリファレンス を学びます


付録:ソースコード参照

クリックしてソースコードの場所を展開

更新時間:2026-01-23

機能ファイルパス行番号
Claude handler:退避再試行戦略 + ローテーションルールsrc-tauri/src/proxy/handlers/claude.rs117-236
Claude handler:内部的に非ストリームをストリームに変換(better quota)src-tauri/src/proxy/handlers/claude.rs665-776
Claude handler:peek 先読み(ハートビート/コメントをスキップ、空ストリーム回避)src-tauri/src/proxy/handlers/claude.rs812-926
Claude handler:400 署名/ブロック順序エラーの修復再試行src-tauri/src/proxy/handlers/claude.rs999-1102
Gemini handler:peek 先読み(空ストリーム 200 OK 防止)src-tauri/src/proxy/handlers/gemini.rs117-149
Gemini handler:400 署名エラーの修復プロンプト注入src-tauri/src/proxy/handlers/gemini.rs300-325
署名キャッシュ(3 層:tool/family/session、TTL/最小長を含む)src-tauri/src/proxy/signature_cache.rs5-207
Claude SSE 変換:署名をキャプチャし署名キャッシュに書き込みsrc-tauri/src/proxy/mappers/claude/streaming.rs639-787

重要な定数

  • MAX_RETRY_ATTEMPTS = 3:最大再試行回数(src-tauri/src/proxy/handlers/claude.rs#L27
  • SIGNATURE_TTL = 2 * 60 * 60 秒:署名キャッシュ TTL(src-tauri/src/proxy/signature_cache.rs#L6
  • MIN_SIGNATURE_LENGTH = 50:署名最小長(src-tauri/src/proxy/signature_cache.rs#L7

重要な関数

  • determine_retry_strategy(...):ステータスコードに基づいて退避戦略を選択(src-tauri/src/proxy/handlers/claude.rs#L117-L167
  • should_rotate_account(...):ステータスコードに基づいてアカウントをローテーションするか決定(src-tauri/src/proxy/handlers/claude.rs#L226-L236
  • SignatureCache::cache_session_signature(...):セッション署名をキャッシュ(src-tauri/src/proxy/signature_cache.rs#L149-L188