500と502は制限ではなく障害を意味します。そのため、この系列のエラーの中でほぼ即座に再試行する価値がある唯一のクラスです。429は自分側のレート制限、529はプロバイダーが処理能力の上限に達していることを示します。この3つのエラークラスのうち、再試行すべきでないものを再試行すると、小さなインシデントが自分のインシデントになります。
エラー
// Straight from the model provider (HTTP 500)
{
"type": "error",
"error": { "type": "api_error", "message": "Internal server error" }
}
// From a gateway or proxy in between (HTTP 502)
{
"error": {
"message": "Failed to reach upstream provider",
"type": "upstream_error",
"code": "upstream_error",
"param": null
}
}原因と対処法の概要
| 原因 | 対処法 |
|---|---|
| 一時的なプロバイダー側の障害 | 指数バックオフとジッターを使って再試行し、約5回を上限にします。 |
| 接続は受け付けられたが、その後応答がない | エラーではなくハングです。最初の1バイトまでの時間と合計時間を別々に制限します。 |
| 中間サービスが返す独自の502 | モデルとは無関係です。本文がプロバイダーの形式か、プロキシの形式かを確認します。 |
| 実際のインシデント中に無条件で再試行する | 試行回数に上限を設けてバックオフします。そうしなければ、再試行自体が障害の一部になります。 |
対処法を選ぶ前に500と529、429を分ける
429はレート制限を超過している状態です。速度を落としてください。529はプロバイダーが容量上限に達している状態です。より大幅に、より長くバックオフしてください。500/502は障害で、通常は短時間で解消し、多くの場合は特定のリクエストに限られます。すぐに再試行する価値があるのは3つ目の500/502だけです。3つを同じように扱うことが、再試行ループによってインシデントが悪化する理由です。
5xxは再試行し、4xxは決して再試行しない
ジッター付き指数バックオフで、最大5回まで試行します。同じヘルパーをすべてのプロバイダーで使えます。400や422は次の試行でも同じように失敗するため、再試行しても同じエラーに到達するまでの遅延を増やすだけです。
import time, random
from openai import OpenAI, APIStatusError
client = OpenAI(base_url="https://api.kunavo.com/v1", api_key="sk-kn-...")
def with_backoff(fn, max_retries=5):
for attempt in range(max_retries):
try:
return fn()
except APIStatusError as e:
if e.status_code not in (429, 500, 529):
raise # don't retry auth/validation errors
retry_after = e.response.headers.get("retry-after")
delay = float(retry_after) if retry_after else min(2 ** attempt, 30)
time.sleep(delay + random.uniform(0, 0.5)) # jitter avoids herds
raise RuntimeError("retries exhausted")
resp = with_backoff(lambda: client.chat.completions.create(
model="claude-sonnet-5",
messages=[{"role": "user", "content": "ping"}],
max_tokens=32,
))
print(resp.choices[0].message.content)最初のバイトまでの時間を合計時間とは別に制限する
リクエスト全体に1つのタイムアウトを設定すると、長時間の生成と応答しなくなった接続を区別できません。最初のバイトには短い期限を、残りには余裕のある期限を設定します。これにより、ハングは速やかに失敗として処理され、本当に時間のかかる回答はそのまま待てます。
試行ごとにステータスとレイテンシーを記録する
試行ごとの記録がなければ、プロバイダーのインシデントと自分のタイムアウトは、後から見ると同じに見えます。ステータスコード、レイテンシー、試行番号だけで、翌朝には両者を区別できます。
Kunavo経由で呼び出している場合
2026年9月現在、Kunavo上のすべてのClaudeモデルは単一のアップストリームチャネルを介して提供されるため、そのチャネルからの5xxはリクエスト内で再試行されません。代わりに、メッセージ「Upstream provider error」を含む502として返され(/v1/messagesでは型api_error)、リクエストはコスト0で記録されます。Kunavoのリクエスト内再試行は、2つ目のチャネルが設定されたモデルに対してのみ実行されます。その場合、タイムアウト、5xx、429、またはKunavo独自のアップストリームキーの拒否は、あなたに届く前にそのチャネルで再試行されます。対象は/v1/messages、/v1/responses、および/v1/chat/completions上のClaudeモデルです。ストリームは最初のコンテンツが到着するまで保留されるため、まだ開始していないストリーム内のエラーも再試行されます。コンテンツの送信が始まった後の途中の失敗は、あなたの側で処理してください。いずれの場合も、このページの再試行ポリシーは自分の側にも維持してください。 この動作の背後にあるルーティングについては、 AIゲートウェイガイド.
よくある質問
500を返すリクエストにも課金されますか?
Kunavoでは、いいえ。失敗したリクエストはコスト0で記録されます。プロバイダーと直接契約して利用する場合の課金はプロバイダーによって異なりますが、通常5xxに料金はかかりません。
500を再試行すると、完了結果が2つ生成されることはありますか?
はい。モデルがすでに生成した後にリクエストが失敗することがあります。副作用がある処理では、再試行を追加する前に、アプリケーション層で冪等性を確保してください。
500、502、529の違いを一言で言うと?
500はプロバイダー自体の障害、502はその手前にあるものがプロバイダーへ到達できない障害、529はプロバイダーが容量上限に達している状態です。最初の2つはすぐ、3つ目はかなり時間を置いて再試行します。