错误码
每个错误码该怎么处理,以及哪些值得重试。
错误是 OpenAI 兼容的形状,另附 request_id:
json{
"error": {
"message": "请求过于频繁,请稍后再试",
"type": "rate_limit_error",
"code": "rate_limited",
"request_id": "req_88a1",
"details": { "retry_after_seconds": 12 }
}
}code是小写的,按它分支,不要按message分支——message是给人看的,会改;- 参数错误时
param指出是哪个字段; details是附加信息(例如retry_after_seconds),官方 SDK 会忽略它,不影响兼容。
报障时请带上 request_id(响应头 X-Request-Id 里也有)。它是我们能定位到那一次具体调用的唯一线索,
没有它只能靠时间范围模糊查。
该重试的
| 状态码 | code | 怎么处理 |
|---|---|---|
| 429 | rate_limited | 按响应头 Retry-After 等待后重试。不要固定间隔猛重试。 |
| 502 | upstream_error | 上游返回了错误,指数退避重试。我们内部已经转移过了。 |
| 503 | model_unavailable | 这个模型暂时没有可用的上游。有 Retry-After 就按它等,没有就指数退避。 |
| 504 | upstream_timeout | 同 502。如果持续出现,考虑调小 max_tokens。 |
| 409 | idempotency_conflict | 带了 Idempotency-Key、而同一个键的请求还在处理中:等一会儿再试,并设重试上限(见下表同名的一行)。 |
不该重试的
| 状态码 | code | 怎么处理 |
|---|---|---|
| 400 | validation_failed | 请求体有问题,重试多少次都一样。看 message 与 param。上游拒绝了你的参数时也是这个码。 |
| 401 | unauthenticated | 没带 Key、格式不对,或 Key 不存在。不要自动重试,检查环境变量。 |
| 401 | key_revoked / key_expired | Key 已被吊销或过期。去控制台换一把。 |
| 401 | account_suspended | 账号已被停用。联系我们,不要重试。 |
| 402 | insufficient_quota | 配额不足。充值后再试,或联系管理员。 |
| 403 | model_not_allowed | 这个 Key 的模型白名单里没有你请求的模型。 |
| 404 | model_not_found | 模型 id 写错了,或该模型已下线。 |
| 409 | idempotency_conflict | 同一个 Idempotency-Key 用在了不同的请求体上:多半是代码把键复用错了。和上表「处理中」是同一个 code,只有 message 不同——有限次重试后仍是 409,就按这一行处理。 |
| 413 | payload_too_large | 请求体超过上限。拆小再发。 |
| 429 | key_quota_exceeded | 这把 Key 的月度消费上限用完了,到下个月或在控制台调高上限。 |
| 429 | project_budget_exceeded | 项目的月预算用完了,Retry-After 指向下个月一号。 |
同是 429,rate_limited 过几秒就好,另外两个要等到下个月——按 code 分支,别只看状态码。
关于重试
我们在网关侧已经做了一层故障转移:一个上游接口超时或返回 5xx 时, 会自动转到下一个可用接口(次数有上限,且总耗时有预算)。
所以你收到 5xx 时,说明我们这边已经试过不止一次了。
你自己那层重试请用指数退避,不要立刻重发——那只会让已经拥塞的上游更拥塞。
非流式请求重试时带上同一个 Idempotency-Key,就不会因为「其实第一次已经成功了」而被扣两次费——
前提是第一次没有被你这边先断开(断开的请求服务端会取消、不保存,见「缓存与幂等」)。
流式请求里的错误
流式响应可能在已经吐出一部分内容之后才出错。这时候 HTTP 状态码已经是 200 了,
错误以一个带 error 字段的分片出现在流里,之后流直接结束,不会再有 data: [DONE]:
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"这段序列的"}}]}
data: {"error":{"message":"上游模型服务响应超时","type":"upstream_error","code":"upstream_timeout","request_id":"req_88a1"}}
请处理这种情况。只看 HTTP 状态码的话,你会把一段被截断的回答当成完整的;
没收到 [DONE] 就结束的流,一律按失败处理。