错误码
错误响应的几种形态、状态码与 type 取值、各入口的差异,以及哪些错误可以重试
这一页按代码实际行为整理;请求头、响应头、限流与余额处理等平台层面的行为见平台扩展与差异。先说两件事:
- 请求经过两层,网关(鉴权、限流、余额、请求体检查)和路由层(选上游、调上游、故障切换)。两层生成的错误形态不完全一样,而且路由层会把上游返回的状态码原样带给你。
error.code不可靠。网关生成的错误没有code;路由层生成的code有时等于type,有时是更细的值。分支判断请用 HTTP 状态码加error.type,不要依赖code。
每个响应都带 X-Request-ID 头(你自己传了就原样回显)。报问题时附上它。
怎么判断
- 看状态码。
2xx成功;4xx一般是请求本身的问题,不要原样重试;429、5xx才值得重试,见重试建议。 - 看响应体形态(见下节)。不要假设一定是
{"error": {...}}:/v1/messages上是 Anthropic 风格的对象,原生入口的创建阶段错误是各自厂商的格式。 - 流式请求要看流本身。HTTP 已经返回
200之后的失败只会出现在流里,状态码不会变,见流式中途失败。
错误结构
OpenAI 风格(默认)
/v1/chat/completions、/v1/responses、/v1/embeddings、图像、语音等入口,以及网关层的所有错误,都是这个形态:
{
"error": {
"type": "invalid_request_error",
"message": "unknown model: foo-1"
}
}| 字段 | 说明 |
|---|---|
error.type | 错误类别,见 type 取值 |
error.message | 说明文字,面向人,不要解析 |
error.code | 网关层错误没有这个字段;路由层错误有,可能等于 type,也可能是更细的值(如 no_upstream_available)。GET /v1/models/{id} 的 404 另带 param: null |
没有单独标出出错参数的 error.param,出错的字段看 message。
Anthropic 风格(/v1/messages 与 /v1/messages/count_tokens)
{
"type": "error",
"error": { "type": "invalid_request_error", "message": "max_tokens is required and must be > 0" }
}这两个入口上,网关层错误(鉴权、余额、限流、请求体过大、组织停用等)和路由层错误都是这一种形态,type 取 Anthropic 官方的名称:400 为 invalid_request_error,401 为 authentication_error,402 为 billing_error,403 为 permission_error,404 为 not_found_error,413 为 request_too_large,429 为 rate_limit_error,503 为 overloaded_error(当错误本身已是某个官方名称时保持不变),504 为 timeout_error,其余 5xx 为 api_error。
组织或团队停用
组织、团队被停用,或 Key 没有归属组织时,状态码是 403,响应体与其它网关错误一样是标准错误对象,形态随入口而定(/v1/messages 为 Anthropic 风格,其余入口为 OpenAI 风格,Gemini 入口为 Google 风格),type 为 permission_error,message 为 organization suspended、team suspended 或 API key is not attached to an organization。
原生入口的创建阶段错误
原生入口只有平台自己做的参数校验用厂商原生格式,其余错误(见下节"入口差异")仍是 OpenAI 风格。
| 入口 | 参数校验错误的形态 |
|---|---|
/api/v1/services/aigc/*(百炼,含 Wan、HappyHorse、Qwen Image) | {"code": "InvalidParameter", "message": "..."};任务不存在时 {"code": "NotFound", ...} |
/v1/contents/generations/tasks、/api/v3/contents/generations/tasks(方舟 Seedance) | {"error": {"code": "InvalidParameter", "message": "..."}};任务不存在时 code 为 NotFound |
/v2/video_generation(MiniMax) | {"type": "error", "error": {"type": "bad_request_error", "message": "...", "http_code": "Bad Request"}};任务不存在时 type 为 not_found_error |
状态码
| 状态码 | 产生层 | 触发条件 | 你该怎么办 |
|---|---|---|---|
400 | 网关 | 请求里取不到 model(缺字段、请求体不是合法 JSON,或分块上传的请求体超限时也是这个提示);model 不是当前可调用的模型(unknown model: ...) | 检查请求体和模型名,不要重试 |
400 | 路由层 | 请求体绑定失败或校验失败(messages 为空、角色非法、Anthropic 缺 max_tokens 等),message 是具体原因 | 按 message 修请求 |
400 | 上游透传 | 上游判定请求有问题(参数不支持、上下文超长、内容审核等) | 按 message 修请求 |
401 | 网关 | 没带 Authorization: Bearer ...;Key 格式不对(须以 sk-ms- 或旧版 sk-relay- 开头,且长度不少于 20);Key 不存在、已撤销或已过期(这三种返回同一句 invalid API key) | 检查请求头与控制台里的 Key 状态。x-api-key 头不被接受 |
402 | 网关 | 组织余额耗尽,或某个预算信封(Key / 成员 / 团队)额度用完。余额耗尽后,只读查询(GET /v1/models、视频任务和响应的查询等)仍可正常使用,只有会产生费用的请求返回 402 | 充值或调整预算后再请求 |
403 | 网关 | 组织或团队被停用、Key 没有归属组织(permission_error);Key 的模型白名单不含该模型(model_access_error) | 联系组织管理员,不要重试 |
403 | 上游透传 | 上游返回 403,通常 type 为 permission_denied_error | 不是你的 Key 的问题,带 X-Request-ID 联系支持 |
404 | 网关 | GET /v1/models/{id} 的模型不存在或未发布,code 为 model_not_found;/v1/videos* 已下线,code 为 endpoint_retired | 核对模型名 / 改用模型对应的原生入口 |
404 | 路由层 | 模型存在但当前没有任何上游配置(code: model_not_found);视频/响应任务的 ID 不存在或已过期(not_found,原生入口用各自格式) | 核对 ID;任务有查询期限 |
404 | 框架 | 路径不存在或方法不对:响应体是纯文本 404 page not found,不是 JSON | 核对路径和方法 |
413 | 网关 | 请求头声明的 Content-Length 超过上限(网关默认 50 MB,部署可调),invalid_request_error。公网入口前面的 nginx 另有一道 25 MB 上限,超过时由 nginx 直接返回 413,响应体是 HTML,不是 JSON | 缩小请求体,大文件别用 base64 内联;按 25 MB 以内准备请求 |
422 | 上游透传 | 平台自己不生成 422。上游返回时按上游状态码原样透传 | 按 message 修请求 |
429 | 网关 | 触发 RPM 或 TPM 限流(rate_limit_error,message 固定为 too many requests,不区分是 RPM 还是 TPM),带 Retry-After;Realtime 并发会话超限也是 429(无 Retry-After) | 按 Retry-After 退避 |
429 | 上游透传 | 上游限流(含冷却期内只有一个上游的模型);带 Retry-After(上游给了就用上游的,否则默认 5) | 按 Retry-After 退避 |
429 | 公网入口 | 同一个来源 IP 请求过密(约 30 次/秒,允许突发约 100 次)时,入口 nginx 直接返回 429,响应体是 HTML,不带 Retry-After;与 Key 的 RPM 限流是两回事 | 降低并发,指数退避 |
500 | 网关 | 网关构造上游请求失败(server_error) | 可重试;持续出现联系支持 |
500 | 路由层 | 其余未分类的内部错误(internal_error);视频、响应任务的查询或删除失败(上游返回 4xx 时也会统一成这个 500)。message 固定为 The request could not be completed; please retry later.,不回显内部原文 | 同上 |
502 | 网关 | 连不上路由层,或 120 秒内没拿到路由层的响应头(server_error,message 为 upstream service unavailable) | 可重试,非流式的长请求改用 stream: true |
502 | 路由层 | 上游网络层失败(连接、DNS、TLS、超时),type 为 bad_gateway,message 固定为 The upstream service is temporarily unavailable. Please retry. | 可重试 |
503 | 网关 | 见下方"503 的几种来源" | 见下方 |
503 | 路由层 | 没有可用上游:code: no_upstream_available;message 固定为 The upstream service is temporarily unavailable. Please retry later.,带 Retry-After(默认 5) | 稍后重试 |
408、504 等 | 上游透传 | 上游自己返回的状态码。embeddings 与 rerank 会把上游的 408/504 统一成 408 timeout_error,其余入口原样透传 | 可重试 |
路由层返回的上游错误,来自最后一次尝试的那个上游:平台会先切换上游,全部失败后把最后一个上游的状态码和说明返回给你。所以同一个 401、402、403,既可能是网关对你的 Key 的判定,也可能是上游对平台账号的判定。网关的 401 文案是固定的几句(见上表),402 的 type 固定为 insufficient_quota(/v1/messages 上为 billing_error);不符合这些特征的,是上游透传,见下节。
503 的几种来源
type | code | message 特征 | 含义 | Retry-After |
|---|---|---|---|---|
overloaded_error | 无 | gateway at capacity, retry later | 网关并发已满(默认 500 路上游请求,排队最多 10 秒) | 有,固定 5 |
service_unavailable | 无 | service temporarily unavailable, please retry shortly | 网关暂时无法保证 Key 撤销和停用及时生效,所以拒绝服务(出现在鉴权之前) | 有,固定 5 |
api_error | 无 | the platform cannot evaluate this key right now | 网关缺少判定这把 Key 所需的数据(刚启动或同步未完成) | 无 |
service_unavailable | no_upstream_available | The upstream service is temporarily unavailable. Please retry later. | 路由层选不出上游 | 有,默认 5(路由层有明确等待时间时用它) |
| 上游自己的 type | 视情况 | 上游的文案 | 上游返回 503(Anthropic 上游的 529 也会变成 503) | 无 |
这几种都值得重试,但只有第一种明确给出了等待时间。
type 取值
网关生成
type | 状态码 | 说明 |
|---|---|---|
authentication_error | 401 | Key 缺失、格式错误或无效 |
model_access_error | 403 | Key 所属组织或团队的模型白名单不含该模型 |
insufficient_quota | 402 | 余额或预算信封不足(/v1/messages 上为 billing_error);message 为 insufficient balance,预算信封触发时附 (budget envelope: ...) |
rate_limit_error | 429 | RPM / TPM 限流,或 Realtime 并发会话超限 |
invalid_request_error | 400 / 404 / 413 | 缺 model、未知模型、请求体过大、已下线路径 |
overloaded_error | 503 | 网关并发已满 |
service_unavailable | 503 | 配置同步中断 |
api_error | 503 | 网关无法判定这把 Key |
server_error | 500 / 502 | 网关到路由层之间的故障 |
路由层生成
type | 状态码 | code | 说明 |
|---|---|---|---|
invalid_request_error | 400 | 同 type | 请求绑定或校验失败 |
invalid_request_error | 404 | model_not_found | 模型下没有上游 |
service_unavailable | 503 | no_upstream_available | 没有可用上游 |
service_unavailable | 503 | upstream_cooling_down | 可用上游都处于临时冷却,稍后重试 |
bad_gateway | 502 | bad_gateway | 上游网络层失败 |
internal_error | 500 | internal_error | 未分类内部错误 |
not_found | 404 | 同 type | 响应、视频任务的 ID 找不到 |
no_upstream_available 与 upstream_cooling_down 两个错误码于 2026-10-19 更名,旧名 no_suppliers_available / all_vendors_cooling_down 含义相同;若你的客户端按旧码判断,请同时匹配新旧两个名字。
上游错误经路由层透传
状态码取上游的,message 取上游的文案(会去掉请求 ID、内部端点 ID 等标识)。type 取决于入口和上游类型:
| 入口 | 上游状态码 | type |
|---|---|---|
| chat / messages / responses,OpenAI 兼容类上游(多数国内供应商) | 任意 | api_error,状态码原样 |
| chat / messages / responses,Anthropic 类上游 | 400 401 403 404 429 500 | invalid_request_error authentication_error permission_denied_error not_found_error rate_limit_error internal_server_error;529 变 503 service_unavailable;其余 api_error |
| 图像、视频创建(百炼、方舟这几类) | 400 401/403 404 429 500 | invalid_request_error authentication_error(注意 403 也映射成 401)not_found_error rate_limit_error internal_server_error;其余 api_error |
| embeddings、rerank | 按状态码和文案 | rate_limit_error、context_length_exceeded、content_policy_violation、invalid_request_error、authentication_error、permission_denied_error、not_found_error、timeout_error(408)、internal_server_error、service_unavailable、api_error |
context_length_exceeded 和 content_policy_violation 只在 embeddings、rerank 上出现。对话类入口遇到上下文超长或内容审核,你收到的是 400 加 api_error 或 invalid_request_error,原因在 message 里。
各入口的差异
| 入口 | 网关层错误 | 路由层 / 上游错误 | 流式中途失败 |
|---|---|---|---|
/v1/chat/completions | OpenAI 风格 | OpenAI 风格,含 code | data: {"error":{"message","type"}} 然后 data: [DONE] |
/v1/messages、/v1/messages/count_tokens | Anthropic 风格 | Anthropic 风格 | event: error,数据为 {"type":"error","error":{"type","message"}} |
/v1/responses | OpenAI 风格 | OpenAI 风格,含 code | event: error({"type":"error","code","message"})后跟 event: response.failed |
/v1/embeddings、/v1/rerank、图像、语音 | OpenAI 风格 | OpenAI 风格,含 code | 不适用 |
| 视频创建与任务轮询(百炼、方舟、MiniMax 原生入口) | OpenAI 风格 | 参数校验与任务不存在用原生格式;选不到上游或上游调用失败仍是 OpenAI 风格 | 不适用 |
视频与异步任务要注意:
- 创建请求成功只表示任务已提交,HTTP 状态是
200。任务后来失败时,轮询接口仍返回200:百炼入口里output.task_status为FAILED,原因在output.code和output.message;方舟、MiniMax 入口返回上游任务体,status为failed,错误字段是上游自己的。 - 轮询时任务 ID 无效或过期,用的是原生入口各自的
404格式;轮询时上游调用本身出错,则是 OpenAI 风格的500或502。 /v1/videos、/v1/videos/generations已下线,返回404,code为endpoint_retired。视频只在各模型的原生入口提供。
GET /v1/responses/{id} 通过响应 ID 找回创建它的上游。ID 无法解码时是 400,找不到上游是 404(type: not_found)。DELETE /v1/responses/{id} 和 POST /v1/responses/{id}/cancel 不需要请求体,也不需要 model,网关直接放行到后端;它们不产生费用,余额耗尽时同样可用。
流式中途失败
流一旦开始,HTTP 状态已经是 200,之后的失败只能写进流里。三种情况:
- 上游中途报错、连接中断、流被截断(没有收到结束事件就结束了)、或默认 4 分钟没有任何数据:按上表的入口格式发出一个错误事件,然后结束流。
type是上游的错误类型,网络层或截断统一为server_error。 - 组织余额在流进行中耗尽:网关切断流,并写入
data: {"error":{"type":"insufficient_quota","message":"balance exhausted"}}和data: [DONE]。这一帧在所有流式入口上都是这个 OpenAI 风格,包括/v1/messages。 - 你主动断开:没有任何错误事件。
处理方式:把"没有收到正常结束标志"当成失败。Chat Completions 是最后一个 chunk 没有 finish_reason 或出现了 error 帧,Anthropic 是没有 message_stop,Responses 是没有 response.completed。中途失败时,已经生成的部分仍按上游实际用量计费。
在收到第一个字节之前失败,平台会先自动切换上游,成功就正常返回,全部失败才返回一个带状态码的 JSON 错误。
重试建议
| 状态码 | 重试? | 说明 |
|---|---|---|
400 401 402 403 404 413 | 否 | 原样重试结果不会变。402 充值后才会好 |
429 | 是 | 有 Retry-After 就按它等,没有就指数退避。被 TPM 限流时,Retry-After 可能比实际需要等待的时间短,退避要留余量 |
500 502 503 504 408 | 是 | 指数退避,加抖动,最多几次 |
任何状态码,但 type 为 api_error 且是 401 402 403 | 否 | 这是上游对平台账号的判定,不是你的 Key 或余额,重试没用,联系支持并附 X-Request-ID |
import random, time
RETRYABLE = {408, 429, 500, 502, 503, 504}
def call_with_retry(send, tries=4):
for i in range(tries):
resp = send()
if resp.status_code not in RETRYABLE or i == tries - 1:
return resp
wait = float(resp.headers.get("Retry-After", 2 ** i))
time.sleep(wait + random.random())重复调用与重复计费
- 平台没有幂等键,也不会按请求内容去重。
- 一次请求在平台内部切换上游,只有最终成功的那一次会计费,失败的尝试不计费。你看到的是一次成功或一次错误。
- 你自己重试则是新的请求。如果上一次其实已经成功、只是响应没到你手里(客户端超时、连接被切断),重试会产生第二次生成和第二次计费。对话类请求副作用有限,可以接受;视频创建不能这样重试:创建请求遇到超时、
502或连接中断时,先确认任务有没有建出来,再决定是否重新创建,否则会出现两个任务、两份费用。 - 流式请求中途失败不要整体重试成"同一次生成",已输出的部分已经计费,重试从头再来会再计费一次。需要续写就把已收到的内容放进上下文发新请求。
- 非流式请求有两个相关的时间限制:网关等不到响应头 120 秒就断开并返回
502;路由层调用上游的总时限是非流式 10 分钟、流式 5 分钟。生成时间可能超过两分钟的请求请用stream: true。