うまくいかないとき

エラーを調べる

#エラーを貼り付け

エラーは HTTP ステータス、error.type、error.code の順に読んでください。ステータスは常に意味を持ちます。type は誰が呼び出しを拒否したかを示し、code が安定しているのはゲートウェイ自身が発生させたエラーの場合だけです。

#分岐のしかた

  1. 1

    まずステータスで分岐

    402 はチャージ、403 はキーのスコープ、429 は待機、5xx は再試行を意味します。この層が偽ることはなく、変わることもありません。

  2. 2

    error.type を確認

    xiaoxiang_error は当プラットフォームのものです。それ以外 — openai_error、claude_error、gemini_error — はベンダー独自のボディで、そのまま転送されています。

  3. 3

    次に error.code を読む

    code で分岐するのは xiaoxiang_error の場合だけにしてください。上流の code はベンダーのもので、予告なく変わることがあります。

すべての失敗に JSON ボディがあるわけではない

レート制限による拒否と大きすぎるボディは、ステータス行だけで中断されます。response.json() が常に成功すると想定したパーサーは、エラー時に処理するどころか例外を投げてしまいます。
エラー形式

クライアントに届く形式は 5 つあります。5 つすべてを処理してください — 最後の 3 つは統合で見落とされがちなものです。

ゲートウェイエラー

ゲートウェイ自体が呼び出しを拒否しました — 認証、利用枠、ルーティング、リクエスト形式のいずれかです。

http
{
  "error": {
    "message": "insufficient account balance, please top up to continue (request id: 20260910...)",
    "type": "xiaoxiang_error",
    "code": "insufficient_user_quota"
  }
}

上流エラー(そのまま転送)

ベンダーが独自のエラーを返しました。ボディは変更せずに転送されるため、type と code はベンダーのものです。

http
{
  "error": {
    "message": "This model's maximum context length is 128000 tokens.",
    "type": "invalid_request_error",
    "code": "context_length_exceeded"
  }
}

ガードによる拒否

冪等性チェックなど一部のリレー前ガードは、リレーのエンベロープが作られる前に応答します。フラットな形式で、code は大文字です。

http
{
  "code": "IDEMPOTENCY_IN_PROGRESS",
  "error": "a request with this Idempotency-Key is still processing"
}

非同期タスクの拒否

すべてのタスクエンドポイント — 動画、画像タスク、音楽、3D — は、それぞれ独自のフラットな形式で失敗を報告します。ネストされた error オブジェクトはないため、/v1/chat/completions 向けに書いたパーサーでは null になります。

http
{
  "code": "task_not_exist",
  "message": "task does not exist",
  "data": null
}

ステータスのみ(ボディなし)

レート制限による拒否と大きすぎるリクエストボディは、ステータスのみで中断されます。

http
HTTP/1.1 429 Too Many Requests
Retry-After: 20
Content-Length: 0
レスポンスヘッダー

失敗時だけでなく、すべてのレスポンスでリクエスト ID を記録してください。サポートが呼び出しを追跡するための識別子であり、5 分で回答できるか、丸 1 日推測に費やすかの分かれ目になります。

ヘッダー送信される条件使い方
X-Oneapi-Request-Id成功・エラーを問わず、すべてのレスポンス。自社のトレース ID と並べて記録してください。サポートはこの値をもとにチケットに回答します。同じ ID はゲートウェイのエラーメッセージにも付加されます。
Retry-After429 のとき、および Idempotency-Key の処理がまだ続いている間の 409 のとき。少なくともこの秒数だけ待機してください。ゲートウェイが送るバックオフのヒントはこれだけです。
X-Idempotent-Replayed同じ Idempotency-Key が繰り返し送られ、保存済みのレスポンスをリプレイしたとき。true は新たな処理が実行されず、再課金もされていないことを意味します — ボディは元の結果として扱ってください。
X-Idempotency-Sealed元のレスポンスを安全にリプレイできません(ストリームやサイズが大きすぎるボディなど)。そのキーでの再試行は中止してください。代わりにタスクのエンドポイントか自社の記録から照合してください。
log-the-request-id.ts
const response = await fetch(url, init);
const requestId = response.headers.get("X-Oneapi-Request-Id");

if (!response.ok) {
  // Bodies are absent on 429 and 413 — never assume JSON here.
  const detail = await response.text();
  logger.error({ status: response.status, requestId, detail });
  throw new GatewayError(response.status, requestId, detail);
}

#エラーコード一覧

ゲートウェイ自身が発生させるエラーで、type は常に "xiaoxiang_error" です。2 つの行は同じステータス 429 で意味が逆です — バックオフする前に code を読んでください。

HTTPコード意味対処法再試行可否
401なしBearer トークンがない、形式が不正、削除済み、または無効化されています。この層は code を設定しません — ステータスで判定してください。Authorization ヘッダーと、コンソール → API キー でキーの状態を確認してください。いいえ
401signature_requiredこのキーではリクエスト署名が有効ですが、リクエストに X-Timestamp、X-Nonce、または X-Signature がありません。このキーで行うすべてのリクエストに署名する(認証とキー → リクエスト署名)か、キーの署名を無効にしてください。いいえ
401signature_expiredX-Timestamp が 10 桁の Unix 秒ではないか、サーバー時刻と 5 分以上ずれています。レスポンスには X-Server-Timestamp が含まれます。X-Server-Timestamp との差分でクライアントの時計を補正し、新しい nonce で署名し直してください。はい
401signature_invalidX-Signature がキーの署名シークレットと一致しないか、X-Nonce の形式が不正です。コンソール → API キー にある現在のシークレットで、タイムスタンプ、nonce、メソッド、パス(クエリ文字列なし)を改行で連結したものに署名してください。いいえ
401signature_replayedこの X-Nonce は過去 10 分以内にこのキーで使用済みです。再試行も含め、リクエストごとに新しいランダムな nonce を生成してください。はい
403access_denied呼び出し元 IP がキーの IP 許可リストに含まれていません。コンソール → API キー で送信元アドレスを追加するか、固定 IP がないキーでは許可リストを外してください。いいえ
403なしアカウントが停止されているか、キーにこのモデルまたはチャネルの使用が許可されていません。まずキーのモデル許可リストを確認してください。制限がない場合はサポートチケットを作成してください — アカウント自体がブロックされています。いいえ
400unsupported_endpointモデルは存在しますが、このパスには対応していません。対応しているエンドポイントがメッセージに記載されます。記載されたエンドポイントに呼び出しを移してください — 画像タスクモデルは /v1/chat/completions では応答しません。いいえ
400read_request_body_failedリクエストボディを読み取れませんでした — アップロードの途中切れ、Content-Type の誤り、またはストリームの早期終了です。一致する Content-Type で完全なボディを送信してください。同じ壊れたリクエストを再送しても同様に失敗します。いいえ
402insufficient_user_quotaウォレット残高が、この呼び出しで事前確保する金額に足りません。コンソール → ウォレット でチャージしてから再試行してください。いいえ
429insufficient_user_quotaアカウントの 1 日の利用上限額に達しました。ステータスはレート制限と同じですが意味は逆です — バックオフしても解消されません。アカウント設定で上限を引き上げるか解除するか、翌日に再開してください。429 だけでなく code で分岐してください。いいえ
429なしキー単位の RPM/RPD 制限、または同時実行数のガードです。ボディは空で、待機時間は Retry-After に入ります。Retry-After の秒数だけ待ってから、ジッター付きの指数バックオフで再試行してください。はい
413read_request_body_failedリクエストボディが許容サイズを超えています。転送層の制限により、ボディなしで中断されます。ペイロードを小さくしてください — ファイルは /v1/media/upload-policy でアップロードし、Base64 ではなく URL を送信します。いいえ
422invalid_requestこのモデルのこの生成モードに対応するチャネルがありません — 例:テキストから動画専用のルートで画像から動画を要求した場合。モデルカードで対応する生成モードを確認し、モデルか生成モードを変更してください。いいえ
503model_not_found不明なモデル名か、現在そのモデルを提供できる正常なチャネルがありません。ステータスに注意してください:不明なモデルは 404 ではなく 503 です。/v1/models で名前を確認してから再試行してください — 通常はフェイルオーバーにより、次の試行で正常なチャネルが見つかります。はい
500sensitive_words_detectedプロンプトがゲートウェイを出る前にコンテンツフィルターに引っかかりました。プロンプトを変更してください。同じテキストで再試行しても必ず失敗します。いいえ
500count_token_failed料金計算のためのトークン数をゲートウェイが推定できませんでした。通常は messages 配列の形式不正が原因です。messages 配列を検証してください。形式が正しければ再試行し、リクエスト ID を添えてサポートチケットを作成してください。はい
500invalid_requestリクエストを、このエンドポイントが受け付ける形式に解析できませんでした。ステータスに注意してください — ゲートウェイが読み取れないボディは 400 ではなく 500 として報告されます。再試行の前に、エンドポイントのリファレンスと照らして JSON を検証してください。いいえ
502do_request_failedベンダーに到達できなかったか、ゲートウェイがフェイルオーバーで回復できない失敗が返されました。バックオフして再試行してください — 次の試行は通常、別のチャネルで処理されます。はい
504do_request_failedベンダーがゲートウェイの期限内に応答しませんでした。バックオフを入れて再試行してください。長いプロンプトや混雑しているベンダーではタイムアウトが起きやすくなります。はい
499do_request_failed応答が届く前にクライアントが接続を閉じました — 多くの場合、ストリームの途中で自身の HTTP タイムアウトが発動しています。クライアントの読み取りタイムアウトを延ばしてください。長い生成には数分かかり、多くの HTTP ライブラリのデフォルト値では足りません。いいえ
冪等性ガードのエラー

これらはリレーの前にあるリプレイガードから返されるため、OpenAI のエラー形式ではなく、code が大文字のフラットな {code, error} 形式を使います。

HTTPコード意味対処法再試行可否
400IDEMPOTENCY_STREAM_UNSUPPORTEDストリーミングリクエストに Idempotency-Key が送信されました。ストリーミング呼び出しではこのヘッダーを外すか、リプレイ保護が必要な場合は stream:false にしてください。いいえ
400IDEMPOTENCY_BODY_UNREADABLE冪等台帳用にボディのハッシュを計算できませんでした。通常の JSON または multipart ボディを送信してください — バッファできないチャンク形式のボディは拒否されます。いいえ
409IDEMPOTENCY_PAYLOAD_CONFLICTこのキーはすでに別のボディ、パス、またはクエリで使用されています。新しいキーを生成してください。1 つのキーが識別するのは特定の 1 件のリクエストであり、再試行ループ全体ではありません。いいえ
409IDEMPOTENCY_IN_PROGRESSこのキーを使った最初のリクエストがまだ実行中です。Retry-After(2 秒)待ってから同じキーで再試行してください — 元のレスポンスが返されます。はい
409IDEMPOTENCY_RESULT_UNREPLAYABLE元のリクエストは完了しましたが、レスポンスが封印されておりリプレイできません。やみくもに再送信しないでください。まずタスクのエンドポイントか自社の記録で照合してください。いいえ
503IDEMPOTENCY_UNAVAILABLE冪等台帳が利用できないため、二重課金のリスクを避けてガードはフェイルクローズします。バックオフして再試行してください — このリクエストは実行されていません。はい

冪等キーの仕組み →

非同期タスクのエラー

動画、画像タスク、音楽、3D のエンドポイントは、送信、ポーリング、キャンセルのいずれでも独自のフラットな形式 — {code, message, data} — で応答します。想定しておくべき癖が 1 つあります:不明なタスク ID のポーリングは 400、キャンセルは 404 です。

HTTPコード意味対処法再試行可否
400task_not_exist存在しない、または別アカウントのタスク ID をポーリングまたはリミックスしました。ステータスに注意してください — 不明なタスクのポーリングは 400、キャンセルは 404 です。送信時に task_id を保存し、その値のままでポーリングしてください。いいえ
400invalid_request必須フィールドがありません — リミックスでは prompt、取得では task_id。ボディかパスを修正してから再試行してください。いいえ
400task_channel_disable後続タスク(リミックス、延長)は元のタスクのチャネルに固定されており、そのチャネルが現在無効になっています。古いタスクから派生させず、新しいタスクを送信してください。いいえ
400invalid_source_taskソースタスクがこの操作に合わない種類です — 例:3D の継続処理に動画の ID を渡した場合。ソースの ID が同じ機能ファミリーのものか確認してください。いいえ
404task_not_exist存在しない、または自分のものではないタスク ID をキャンセルしようとしました。キャンセル対象はありません — すでに存在しないものとして扱ってください。いいえ
409task_not_cancellableタスクはすでに終了状態に達しているため、キャンセルできるものはありません。もう一度ポーリングして、支払い済みの結果を使用してください。いいえ
501task_cancel_not_supportedこのタスクの背後にあるプロバイダーはキャンセル API を提供していません。大半がそうです — キャンセルできるのは例外であり、通例ではありません。タスクの完了を待ってください。キャンセルは保証ではなくベストエフォートとなるよう UI を設計してください。いいえ
502cancel_task_failedキャンセル中にプロバイダーに到達できませんでした。タスクは変更されていません。一度だけ再試行し、だめなら完了まで待ってください。はい

非同期タスクのライフサイクル →

まだ解決しませんか?

リクエスト ID かエラーメッセージを送っていただければ、こちらで調査します。