Qwen API には代表的な呼び出し方法が二つあります。阿里雲百煉のネイティブ DashScope インターフェースと、OpenAI 互換インターフェースです。両者のエラー構造は完全には同じではありませんが、切り分けの順序は共通しています。まず HTTP ステータスコードを見て、次にレスポンス本文の code と message を読み、request_id を探し、最小リクエストで再現してから、最後にパラメータやモデルを変更します。
本記事では 401、404、429、400、5xx、タイムアウトに分けて、エラーコードの対応表、ログ項目、再試行コード、サポート連絡前のチェックリストを示します。モデル、割当、課金は公式ドキュメントとモデル価格を基準とします。
注記:Qwen、通義千問、阿里雲、百煉、DashScope はアリババグループおよび関連する権利者の商標です。本記事は技術的なトラブルシューティングの議論のみを目的としており、上記の企業との資本関係、代理関係、提携関係はありません。エラーコード、レート制限の基準、インターフェースの挙動はバージョンによって変わる場合があるため、最終的には公式ドキュメントと実際のレスポンスに従ってください。
一、まずインターフェースモードとエラー構造を分ける
ネイティブの DashScope インターフェースは、通常レスポンスの最上位に code、message、request_id を返します。OpenAI 互換インターフェースは OpenAI 形式を踏襲し、エラー本文は {"error": {"message": "...", "type": "...", "code": "..."}} のような形になります。互換モードの request_id はレスポンスヘッダーまたは本文に現れることがあり、フィールド名は実際の返却値に従ってください。
実装上は一つの構造だけを解析しないでください。まず完全なレスポンス本文をそのままログに記録し、error.code、最上位の code の順に値を取得します。そうすればインターフェースモードを切り替えても、エラー処理コードを書き直す必要がありません。
二、401:認証失敗。まずリージョンを確認する
401 は身元の検証に失敗したことを示し、モデルの能力や残高とは通常関係ありません。百煉でよくあるのは次の三種類です。
InvalidApiKeyまたはinvalid_api_key:Key が誤っている、コピーが不完全、あるいは Base URL と Key の所属リージョンが一致していない。invalid access token or token expired:Token の有効期限切れ、またはパッケージ専用アドレスに通常の Key を設定している。NOT AUTHORIZED:WorkspaceId が無効、またはアカウントがそのワークスペースに属していない。
Qwen の API Key はリージョンに紐付いており、北京とシンガポールの Key は混在できません。これは最も見落としやすい点です。コードを変えずに Base URL だけを変更すると、安定して 401 が再現します。
切り分けは次の順序で行います。
- リクエストヘッダーが
Authorization: Bearer <key>で、コロンの後に半角スペースが一つあることを確認する。 - 環境変数の先頭部分を出力し、空値、古い値、改行混入がないことを確認する。
- Base URL と Key の所属リージョンが一致しているか照合する。
- Key の種類とインターフェースが合っているか確認する。従量課金、Coding パッケージ、Token パッケージではアドレスが異なる。
- 新しい Key で最小リクエストを行い、Key の問題かプロキシやゲートウェイの問題かを切り分ける。
三、404 と 400:モデル名、有効化状態、コンテキスト
404 の多くはサービス障害ではなく、モデル名または可用性の問題です。ModelNotFound または model_not_found の場合は、まず三つを確認します。モデル名の綴りが正しいか、モデルが有効化されているか、そのモデルが現在のリージョンで利用可能か。model_not_supported は、そのモデルが OpenAI 互換モードに対応していないことを示し、インターフェースかモデルを変更する必要があります。
400 の原因はより多様です。InvalidParameter はパラメータの範囲外で、たとえば temperature や top_p が範囲を超えている場合です。InvalidParameter.NotSupportEnableThinking は、モデルが思考モードのパラメータに対応していないことを示します。コンテキスト超過では通常 Range of input length should be [1, xxx] が返り、基底コードは InternalError.Algo.InvalidParameter です。メッセージ全体の長さがモデル上限を超えている場合もあります。対処は履歴の切り詰め、分割要約、長コンテキストモデルへの変更であり、繰り返し再試行ではありません。
四、429:四種類のレート制限と二種類の割当を分ける
429 は最も誤って処理されやすいエラーです。百煉の 429 には少なくとも次の種類があり、表にはレート制限と混同しやすい滞納・未開通のエラーも載せています。
| HTTP | エラーコード | 意味 | 再試行の可否 |
|---|---|---|---|
| 429 | Throttling.RateQuota、LimitRequests |
毎分リクエスト数(RPM)の超過 | 可。バックオフ後に再試行 |
| 429 | Throttling.AllocationQuota、insufficient_quota |
毎分トークン数(TPM)の超過 | 可。バックオフし入力を削減 |
| 429 | Throttling.BurstRate |
短時間の急増による保護の発動 | 可。送信ペースを平準化 |
| 429 | Throttling.Concurrency |
同時リクエストの過多 | 可。同時実行を下げる |
| 429 | AllocationQuota.FreeTierOnly |
無料枠を使い切り、無料枠のみの設定が有効 | 不可。チャージするか設定を解除 |
| 429 | BudgetLimitExceeded |
アカウント予算の使い切り | 不可。予算を調整 |
| 400 | Arrearage |
アカウントの滞納または状態異常 | 不可。まずアカウントを復旧 |
| 403 | AccessDenied.Unpurchased |
サービス未開通 | 不可。まず開通する |
レート制限は阿里雲のメインアカウント単位で集計され、RAM サブアカウント、ワークスペース、複数 Key の呼び出しは合算されます。そのため「サービスごとに Key を一つ」では総枠を回避できず、相互の圧迫を避けられるだけです。レート制限は通常は分単位で回復するため、待機中に高頻度で再試行しないでください。
五、タイムアウトと 5xx:三段階に分けて特定する
タイムアウトは三段階で見ます。接続タイムアウトは通常 DNS、出口、プロキシへの到達不能です。最初のトークンが遅い場合は上流の待ち行列か入力過多が多く、読み取りタイムアウトは出力が長すぎるか推論時間が長い可能性があります。三者の対処は異なり、タイムアウト値だけを大きくすればよいわけではありません。
百煉の RequestTimeOut はリクエストタイムアウトを示し、公式説明ではリクエスト制限時間は 300 秒です。GatewayTimeout.InputDownload は入力ファイルのダウンロードに関係します。OpenAI 互換インターフェースでは 503 が返ることもあり、エンジンの過負荷を示します。ストリーミングが中断した場合はエラーコードがないことがあるため、まず受信済み内容、最後の断片、終了理由を記録してから、ネットワークの揺らぎ、プロキシの切断、上流タイムアウトのどれかを判断します。
六、request_id とログ:トラブルシューティングの証拠
公式説明によると、request_id はレスポンスとともに返され、レスポンスヘッダーまたは本文に含まれる場合があります。実装では二経路で記録することをおすすめします。クライアント側で trace_id を生成してすべての再試行を通し、同時にレスポンスヘッダーのリクエスト識別子と本文の request_id をそのまま保存します。記録がない場合は、コンソールのモデル監視ログで探せますが、推論ログが有効になっていることが前提です。
ログには少なくとも次を含めます。時刻、trace_id、Base URL、リージョン、モデル名、HTTP ステータスコード、エラーコード、エラーメッセージ、request_id、所要時間、再試行回数、ストリーミングかどうか。Key は先頭数文字またはハッシュのみを記録し、ユーザーの原文と完全なレスポンスを丸ごと保存しないでください。サポートに連絡するときは、「ときどきエラーが出る」よりも、これらの項目を含む一件の記録のほうがはるかに役立ちます。
七、再試行とバックオフ:サンプルコード
次のコードは SDK の自動再試行を無効にし、回復可能な 429 と 5xx のみを再試行し、ステータスコード、エラーコード、request_id をまとめて出力します。滞納、無料枠の使い切り、予算超過は再試行の対象外です。
import os
import random
import time
import uuid
import httpx
from openai import OpenAI, APIConnectionError, APIStatusError, APITimeoutError
client = OpenAI(
api_key=os.environ["DASHSCOPE_API_KEY"],
base_url=os.environ.get(
"DASHSCOPE_BASE_URL",
"https://dashscope.aliyuncs.com/compatible-mode/v1",
),
timeout=httpx.Timeout(connect=5.0, read=180.0, write=30.0, pool=10.0),
max_retries=0, # SDK の自動再試行を無効化し、下のロジックで一元管理する
)
RETRYABLE_429 = {
"Throttling",
"Throttling.RateQuota",
"Throttling.AllocationQuota",
"Throttling.BurstRate",
"Throttling.Concurrency",
"LimitRequests",
"limit_requests",
"ResourceExhausted",
}
def unpack_error(exc: APIStatusError):
body = {}
try:
body = exc.response.json()
except Exception:
pass
err = body.get("error", body) if isinstance(body, dict) else {}
code = err.get("code") if isinstance(err, dict) else None
request_id = None
if isinstance(body, dict):
request_id = body.get("request_id")
request_id = (
request_id
or exc.response.headers.get("x-request-id")
or exc.response.headers.get("x-dashscope-request-id")
)
return code, request_id, exc.response.text[:500]
def ask(prompt: str, model: str, max_attempts: int = 4) -> str:
trace_id = uuid.uuid4().hex[:12]
for attempt in range(1, max_attempts + 1):
try:
resp = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
)
return resp.choices[0].message.content
except APIStatusError as exc:
code, request_id, body = unpack_error(exc)
print(
f"trace={trace_id} attempt={attempt} "
f"http={exc.status_code} code={code} "
f"request_id={request_id} body={body}"
)
retryable = exc.status_code >= 500 or (
exc.status_code == 429 and code in RETRYABLE_429
)
if not retryable or attempt == max_attempts:
raise
except (APITimeoutError, APIConnectionError) as exc:
print(f"trace={trace_id} attempt={attempt} err={type(exc).__name__}")
if attempt == max_attempts:
raise
wait = min(2 ** (attempt - 1), 30) + random.uniform(0, 1)
time.sleep(wait)
raise RuntimeError("再試行回数を使い切りました")
補足の規則が三つあります。サーバーが Retry-After を返した場合はそれに従う。再試行の予算は全体で管理し、複数サービスが同時に流量を増やさないようにする。副作用のあるリクエストは、実行済みかどうかを確認してから再送するか決める。
八、同時実行のガバナンス:再試行回数を増やすより効果的
レート制限の問題は、通常、再試行ではなく入口のガバナンスで解決します。
- 業務ごとに Key を分け、それぞれに同時実行上限を設定し、一つのバッチ処理がオンラインサービスを圧迫しないようにする。
- バッチ処理は分割し、時間帯をずらして、毎時ちょうどの流量パルスを避ける。
- キューでピークを削り、失敗したタスクは遅延キューに入れ、その場で繰り返さない。
- 429 の比率と p95 遅延を監視し、急増時はまず同時実行を下げる。
- オフライン処理は Batch インターフェースを優先し、リアルタイムのレート制限を避ける。
- 代替モデルまたは代替入口を用意し、切り替え前にパラメータの互換性を確認する。
レート制限のしきい値はモデルとアカウントごとに設定され、チャージしても RPM や TPM は自動的に上がりません。より高い枠が必要な場合は商談申請を行い、他人の数値をそのまま使わないでください。
九、サポート連絡前のチェックリスト
- 発生日時、タイムゾーン、障害が集中して発生したかどうか。
- Base URL、リージョン、API パス、モデル名、リクエスト方式。
- HTTP ステータスコード、エラーコード、完全なエラーメッセージ。
request_id。なければ自分で生成したtrace_id。- 再現可能な最小の curl リクエスト。Key はマスキングする。
- ストリーミングか、長コンテキストか、直前に設定を変更したか。
- 影響範囲:単一 Key、単一ワークスペース、それとも全サービスか。
十、よくある質問(FAQ)
1. 429 は必ずレート制限ですか?
いいえ。429 は無料枠の使い切りや予算超過のこともあります。滞納は 400 の Arrearage、サービス未開通は 403 を返す場合があります。まずエラーコードを読み、バックオフ、チャージ、設定変更のどれが必要か判断してください。
2. 401 で新しい Key に変えても失敗する場合は?
リージョンと Key の種類を重点的に確認してください。Qwen の Key はリージョンに紐付き、北京の Key にシンガポールのアドレスを設定すると 401 になります。パッケージ専用アドレスに通常の Key を設定しても失敗します。あわせて、リクエストヘッダー、環境変数、リバースプロキシが認証情報を破棄していないか確認してください。
3. コンテキスト超過はどう処理しますか?
まず履歴メッセージを切り詰め、無関係な添付と重複段落を削除します。次に長い文書を分割要約するか検索方式にします。それでも足りない場合は長コンテキストモデルに変更します。max_tokens を増やして入力超過を解決しようとしないでください。
4. タイムアウトは再試行すべきですか?
接続失敗と読み取りタイムアウトに限り、回数を制限して再試行し、指数バックオフと上限を設定します。副作用のある業務では、実行済みかどうかを確認してから再送し、二重注文や二重書き込みを避けてください。
5. request_id はどこで探しますか?
まずレスポンスヘッダーと本文を確認し、フィールド名は実際のインターフェースに従います。どちらにも記録がない場合は、コンソールのモデル監視ログを探します。推論ログが有効であることが前提です。サポート連絡時は trace_id と最小再現リクエストを併せて提供してください。
次のステップ
まずZSQH AI Gatewayでテスト用 Key を作成し、401、404、429、コンテキスト超過、タイムアウトをそれぞれ一度ずつ演習してから、本番監視に接続してください。利用可能なモデルと課金方式はモデル価格を参照してください。
