错误码与限流
- 每个状态码意味着什么、该不该重试、怎么重试
- 错误结构分同步与异步两种
- 排查顺序见常见问题
错误格式
同步接口失败时返回统一结构:
{
"error": {
"code": 401,
"message": "身份验证失败,请检查您的API密钥",
"type": "authentication_error"
}
}| 字段 | 类型 | 说明 |
|---|---|---|
error | object | 错误容器,所有失败响应都包在这一层里 |
error.code | integer | HTTP 状态码,与响应状态行一致 |
error.message | string | 错误描述,面向开发者阅读 |
error.type | string | 错误类型,机器可读,用于程序分支判断 |
程序按 type 分支即可,不必解析 message。
不要用 message 做字符串匹配来判断错误原因。文案可能随版本调整,只有 type 与 code 是稳定的契约。
异步任务的错误
异步任务(图像、视频)失败时,查询任务这一步本身是成功的,HTTP 状态是 200,失败信息在任务对象的 status 与 error 里:
{
"code": 200,
"data": {
"id": "task_01KA",
"status": "failed",
"error": {
"code": "content_policy_violation",
"message": "提示词触发内容审核",
"type": "invalid_request_error"
}
}
}注意这里的 error.code 是字符串,同步接口的 error.code 是数字,解析时要按两种类型分别处理。完整字段见任务响应。
流式响应中的错误
流式响应开始后发生的错误以 SSE 事件发送,HTTP 状态仍是 200。客户端必须处理流内的错误事件,否则会表现为「输出突然中断」。
Synchronous endpoints return a uniform structure on failure:
{
"error": {
"code": 401,
"message": "Authentication failed, please check your API key",
"type": "authentication_error"
}
}| Field | Type | Description |
|---|---|---|
error | object | Error container; every failed response is wrapped in this |
error.code | integer | HTTP status code, matching the response status line |
error.message | string | Human-readable description for developers |
error.type | string | Machine-readable error type, for branching in code |
Branch on type in your code; there's no need to parse message.
Don't string-match against message to determine the cause. Wording can change between versions; only type and code are a stable contract.
Errors from async tasks
When an async task (image, video) fails, the task-query call itself succeeds — the HTTP status is 200 — and the failure information lives in the task object's status and error:
{
"code": 200,
"data": {
"id": "task_01KA",
"status": "failed",
"error": {
"code": "content_policy_violation",
"message": "The prompt triggered content moderation",
"type": "invalid_request_error"
}
}
}Note that error.code here is a string, while on synchronous endpoints it's a number, so handle both types when parsing. For the full set of fields see the task response.
Errors in streaming responses
Errors that occur after a stream has started are sent as SSE events, and the HTTP status remains 200. Your client must handle in-stream error events, otherwise it looks like "the output just stopped".
状态码速查
| 状态 | type | 含义 | 可否重试 |
|---|---|---|---|
| 400 | invalid_request_error | 参数格式错误,或当前模型不支持该字段 | 否,先修正请求 |
| 401 | authentication_error | Key 缺失、无效或已停用 | 否 |
| 402 | payment_required | 余额不足 | 否,充值后再试 |
| 403 | permission_error | 无访问权限,或分组不允许调用该模型 | 否 |
| 404 | not_found_error | 路径或资源不存在,常见于地址多写或少写 /v1 | 否 |
| 409 | conflict | 资源状态冲突,如任务尚未完成 | 稍后 |
| 429 | rate_limit_error | 触发限流 | 是,退避后重试 |
| 500 | server_error | 服务端异常 | 是 |
| 502 | bad_gateway | 上游不可用 | 是 |
| 503 | service_unavailable | 服务暂时不可用 | 是 |
| Status | type | Meaning | Retry? |
|---|---|---|---|
| 400 | invalid_request_error | Malformed parameters, or the model doesn't support that field | No, fix the request |
| 401 | authentication_error | Key missing, invalid, or disabled | No |
| 402 | payment_required | Insufficient balance | No, top up and retry |
| 403 | permission_error | No access, or the group doesn't allow that model | No |
| 404 | not_found_error | Path or resource not found; usually a stray or missing /v1 | No |
| 409 | conflict | Resource state conflict, e.g. the task isn't finished yet | Later |
| 429 | rate_limit_error | Rate limit hit | Yes, with backoff |
| 500 | server_error | Server-side error | Yes |
| 502 | bad_gateway | Upstream unavailable | Yes |
| 503 | service_unavailable | Service temporarily unavailable | Yes |
429 的处理
429 是最需要区分对待的一种。响应头里带 Retry-After 时按它等待,没有时按指数退避,从 1–2 秒起步。
触发限流通常有三类原因:
- 账户并发过高:同时在跑的请求太多,降低并发或串行化
- 单位时间请求数超限:把请求摊开到更长时间里
- 上游容量饱和:同一分组内的上游账号暂时都在限流,稍等会自动恢复
控制台「用量」页面能看到近期的请求量与失败分布,先确认是哪一类,再决定是降并发还是等一等。
429 is the one that needs the most differentiated handling. If the response carries Retry-After, wait that long; otherwise use exponential backoff starting at 1–2 seconds.
Rate limiting usually has one of three causes:
- Account concurrency too high: too many requests in flight at once; lower concurrency or serialize
- Request count per unit time exceeded: spread requests over a longer period
- Upstream capacity saturated: upstream accounts in the same group are all rate limited for now; it recovers on its own shortly
The "Usage" page in the console shows recent request volume and failure distribution. Confirm which case applies, then decide whether to lower concurrency or just wait.
重试建议
可以重试:429、500、502、503。
不要原样重试:400、401、402、403、404、409。这些是请求本身或账户状态的问题,重试不会变好,先按上表修正。
流式请求:收到流内错误事件后,如果此前没有拿到任何内容,可以按 5xx 处理重试一次;已经收到部分内容时不要盲目重试,会产生重复计费。
按 type 分流的写法:
RETRYABLE = {"rate_limit_error", "server_error", "bad_gateway", "service_unavailable"}
def handle_error(body):
err = body["error"]
if err["type"] in RETRYABLE:
return "retry"
if err["type"] == "authentication_error":
raise SystemExit("检查 API Key")
if err["type"] == "payment_required":
raise SystemExit("余额不足,请充值")
return "fail"网关内部已经在同一分组的多个上游之间做过故障转移,客户端不必把重试间隔设得太短——频繁重试反而容易被判定为异常流量。
Safe to retry: 429, 500, 502, 503.
Don't retry as-is: 400, 401, 402, 403, 404, 409. These are problems with the request itself or the account state; retrying won't help. Fix them per the table above.
Streaming requests: after receiving an in-stream error event, if you hadn't received any content yet you can treat it like a 5xx and retry once; if you already received partial content, don't blindly retry — that leads to double billing.
Branching on type looks like this:
RETRYABLE = {"rate_limit_error", "server_error", "bad_gateway", "service_unavailable"}
def handle_error(body):
err = body["error"]
if err["type"] in RETRYABLE:
return "retry"
if err["type"] == "authentication_error":
raise SystemExit("Check your API key")
if err["type"] == "payment_required":
raise SystemExit("Insufficient balance, please top up")
return "fail"The gateway already fails over between upstream accounts within the same group, so clients don't need very short retry intervals — frequent retries are more likely to be flagged as abnormal traffic.
并发与速率
不同分组、不同模型的上限不一样,具体数值以控制台显示的为准。工程上建议:
- 服务端集成用连接池复用连接,避免每次请求新建
- 批量任务用队列限速,不要一次性打满
- 遇到 429 时先降速,再逐步回升,不要立刻恢复原并发
Limits differ by group and by model; go by the values shown in the console. In engineering terms:
- Server-side integrations should reuse connections through a pool instead of opening a new one per request
- Throttle batch jobs through a queue rather than firing everything at once
- On 429, slow down first and ramp back up gradually; don't jump straight back to the original concurrency
还没解决
写邮件到 [email protected],附上:
- 请求时间与接口路径
- HTTP 状态码与完整错误体
Retry-After等响应头- Key 名称(不要贴完整 Key)
Email [email protected] with:
- When the request happened and the endpoint path
- The HTTP status code and the full error body
- Response headers such as
Retry-After - The key name (never paste the full key)