錯誤與錯誤碼
統一的錯誤 JSON 格式、完整錯誤碼清單,以及重試與處理策略。
錯誤與錯誤碼
所有錯誤都回傳非 2xx 的 HTTP 狀態碼,body 一律是以下結構:
{
"error": {
"code": "insufficient_credit",
"message": "Your credit balance is insufficient for this request."
}
}
code 是穩定的機器可讀字串,請以它做程式判斷;message 是人類可讀說明,措辭可能隨時調整,不要拿來做條件判斷。
錯誤碼清單
| HTTP | code | 意義 | 如何處理 |
|---|---|---|---|
| 400 | invalid_request | 請求格式錯誤:缺少必填欄位、JSON 無法解析、參數型別或範圍不對。 | 修正請求。重試沒有意義。 |
| 401 | unauthorized | 缺少 Authorization 標頭,或格式不是 Bearer <key>。 | 補上正確的標頭。 |
| 401 | invalid_api_key | 金鑰不存在、已刪除,或格式錯誤。 | 檢查金鑰是否完整、有沒有多餘空白,或到 Dashboard 重建。 |
| 403 | api_key_disabled | 金鑰已被停用。 | 到 Dashboard 重新啟用,或改用其他金鑰。 |
| 403 | model_not_allowed | 模型存在,但不在此金鑰的白名單內。 | 調整金鑰的模型白名單,或改用允許的模型。 |
| 404 | model_not_found | 平台上沒有這個模型 ID。 | 以 GET /v1/models 確認正確 ID。注意需要 provider/model 前綴。 |
| 402 | insufficient_credit | 帳戶餘額不足以支付這次請求。 | 到 Dashboard 儲值。 |
| 402 | spend_limit_reached | 已達到此金鑰的每月花費上限。 | 調高上限,或等次月自動重置。 |
| 429 | rate_limited | 請求速率超過限制。 | 依 Retry-After 退避後重試。見 速率限制。 |
| 502 | upstream_error | 上游供應商回傳錯誤、逾時,或暫時無法使用。 | 可安全重試,建議指數退避;或改用備援模型。 |
範例
HTTP/1.1 402 Payment Required
Content-Type: application/json
{
"error": {
"code": "spend_limit_reached",
"message": "This API key has reached its monthly spend limit of $50.00."
}
}
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"error": {
"code": "model_not_found",
"message": "Model 'gpt-4o' does not exist. Did you mean 'openai/gpt-4o'?"
}
}
是否該重試
| 類別 | 錯誤碼 | 重試? |
|---|---|---|
| 用戶端錯誤 | invalid_request、model_not_found | 否。先修正程式。 |
| 認證問題 | unauthorized、invalid_api_key、api_key_disabled、model_not_allowed | 否。需要人為處理。 |
| 帳務問題 | insufficient_credit、spend_limit_reached | 否。需要儲值或調整上限。 |
| 暫時性問題 | rate_limited、upstream_error | 是。指數退避加抖動。 |
處理錯誤(Python)
openai SDK 會把非 2xx 轉成例外,錯誤 body 放在 err.response.json()。
import os
import time
import httpx
from openai import OpenAI, APIStatusError
client = OpenAI(
base_url="https://api.alphacurve.io/v1",
api_key=os.environ["INFERENCE_API_KEY"],
timeout=httpx.Timeout(60.0),
)
RETRYABLE = {"rate_limited", "upstream_error"}
def complete(**kwargs):
for attempt in range(5):
try:
return client.chat.completions.create(**kwargs)
except APIStatusError as err:
try:
code = err.response.json()["error"]["code"]
except Exception:
code = "unknown"
if code not in RETRYABLE:
raise
retry_after = err.response.headers.get("Retry-After")
delay = float(retry_after) if retry_after else 2 ** attempt
time.sleep(delay)
raise RuntimeError("retries exhausted")
處理錯誤(Node / TypeScript)
import OpenAI, { APIError } from "openai";
const client = new OpenAI({
baseURL: "https://api.alphacurve.io/v1",
apiKey: process.env.INFERENCE_API_KEY!,
});
const RETRYABLE = new Set(["rate_limited", "upstream_error"]);
async function complete(
params: OpenAI.Chat.ChatCompletionCreateParamsNonStreaming,
) {
for (let attempt = 0; attempt < 5; attempt++) {
try {
return await client.chat.completions.create(params);
} catch (err) {
if (!(err instanceof APIError)) throw err;
const code = (err.error as { code?: string } | undefined)?.code ?? "unknown";
if (!RETRYABLE.has(code)) throw err;
const wait = 2 ** attempt * 1000 + Math.random() * 250;
await new Promise((r) => setTimeout(r, wait));
}
}
throw new Error("retries exhausted");
}
用 curl 除錯
加上 -i 就能同時看到狀態碼、標頭與 body:
curl -i https://api.alphacurve.io/v1/chat/completions \
-H "Authorization: Bearer $INFERENCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "model": "does-not-exist", "messages": [] }'
回報問題時,請附上回應中的 id(若有)、code、發生時間與所用模型,我們才能快速定位。
串流中的錯誤
串流一旦開始,HTTP 狀態碼就已經是 200。若之後才失敗,錯誤會以一筆 SSE 事件送出:
data: {"error":{"code":"upstream_error","message":"Upstream provider timed out."}}
data: [DONE]
因此串流的解析程式除了處理 choices,也必須檢查 chunk 裡有沒有 error 欄位。