可以据此处理的错误
Hyperouter 的每个错误都使用 OpenAI 的错误对象,并配以对应的 HTTP 状态码。已经能处理 OpenAI 错误的代码,基本也能处理这些错误。本指南说明哪些值得处理、哪些不必处理,最后给出一个重试循环。
错误对象
{
"error": {
"message": "n must be an integer from 1 to 128",
"type": "invalid_request_error",
"code": "invalid_body",
"param": "n"
}
}
| 字段 | 含义 |
|---|---|
message | 说明问题的一句话。请勿解析该字段。 |
type | 错误类别。 |
code | 标识原因的稳定标识符。请以该字段进行匹配。 |
param | 出错的请求字段,或 null。 |
部分错误还带有 generation_id:仍在运行或已经运行过的任务的 id。请用 GET /v1/generations/{id} 轮询它。
值得处理的状态码
| 状态码 | 含义 | 处理方式 |
|---|---|---|
400 | 请求无效。code 指明原因,param 指明字段。 | 修正请求。不要原样重试。 |
401 | API 密钥缺失或无效。 | 检查密钥。 |
402 | insufficient_credits:你的余额或密钥的额度上限,不足以覆盖该请求可能产生的最高费用。 | 充值,或提高密钥的额度上限。 |
403 | 账户已被暂停,或某个功能或模型未对其开通。 | 提交支持工单。 |
404 | id 不存在、id 属于其他账户,或视频已超过其 expires_at。 | 不要重试。 |
409 | 当前状态不允许该调用,或 Idempotency-Key 冲突。 | 见安全重试。 |
429 | 所有服务商均已满载,或你的账户超出速率限制。 | 等待 Retry-After 给出的时间,然后重试。 |
500, 501 | Hyperouter 自身出错,或接口尚未实现。不计费。 | 谨慎重试 500,见下文。 |
503 | 服务商出错或未在时限内响应。 | 重试,见下文。 |
你会遇到的三个 code
insufficient_credits(402)。对于聊天,message 会说明你能负担多少 token。调低max_tokens会降低请求可能产生的最高费用。充值需在控制台中人工申请;用 GET /v1/credits 可以提前发现余额不足。model_not_enabled(403)。该模型需要为你的账户开通访问权限。请提交支持工单申请。不会预扣任何费用,也不会向服务商发送任何内容。rate_limited(429)。你的账户超出了每分钟请求数或并发请求数限制。不会预扣或转发任何内容。响应带有以秒为单位的Retry-After响应头,各等级见速率限制一节。
同一类还包括 venue_busy(429):某个聊天模型的所有服务商都已超出其速率预算。
为什么是 503,而不是 502 或 504
Hyperouter 从不返回 502 或 504。这两个状态码会以我们边缘网络的裸页面形式到达你手中,不带错误响应体,你就无法读取 code。
在网关本应返回它们的场景下,你会收到带有常规错误对象的 503,网关状态码则放在响应头 X-Upstream-Status 中:服务商出错时为 502,超时时为 504。code 指明具体是哪一种失败:all_venues_failed、upstream_error 或 timeout。
HTTP/2 503
x-upstream-status: 504
如果想区分出错和超时,请读取 X-Upstream-Status。对大多数代码来说,503 和 code 就足够了。
什么时候不要重试
重试并不总是安全的。重试可能启动第二个任务,或产生第二次扣费。Hyperouter 会在这类响应上附带 x-should-retry: false:
- 同步图像请求的
503timeout。任务仍在继续,成功后会计费。请改为轮询error.generation_id; - 任务创建之后出现的
500; 409idempotency_key_reused和idempotency_key_replayed。
对于聊天,已接受请求的服务商可能正在运行它。如果它随后不再响应,该请求不会被转发给其他服务商:响应为 503,code 为 timeout 或 upstream_error,并按其预扣金额计费。在决定重新发送之前,请先用 GET /v1/generations/{id} 查询它。
带退避的重试循环
对 429 和 503 重试,遵循 Retry-After,遇到 x-should-retry: false 就放弃,并对任何会产生费用的请求发送 Idempotency-Key。
import random import time import uuid import requests def post_with_retry(url, headers, body, attempts=5): headers = {**headers, "Idempotency-Key": str(uuid.uuid4())} # same key on every attempt for attempt in range(attempts): r = requests.post(url, headers=headers, json=body, timeout=120) if r.status_code < 400: return r.json() if r.headers.get("x-should-retry") == "false": r.raise_for_status() if r.status_code not in (429, 503) or attempt == attempts - 1: r.raise_for_status() wait = float(r.headers.get("Retry-After", 2 ** attempt)) time.sleep(wait + random.random()) # jitter
async function postWithRetry(url: string, body: unknown, attempts = 5) {
const headers = {
Authorization: `Bearer ${process.env.HYPEROUTER_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(), // same key on every attempt
};
for (let attempt = 0; ; attempt++) {
const res = await fetch(url, { method: "POST", headers, body: JSON.stringify(body) });
if (res.ok) return res.json();
const retryable = [429, 503].includes(res.status) && res.headers.get("x-should-retry") !== "false";
if (!retryable || attempt === attempts - 1) throw new Error(`HTTP ${res.status}`);
const wait = Number(res.headers.get("Retry-After") ?? 2 ** attempt);
await new Promise((r) => setTimeout(r, (wait + Math.random()) * 1000));
}
}
对于聊天请求,如果首次尝试确实已经运行,重试会收到 409 idempotency_key_replayed。这就是该用 GET /v1/generations/{id} 读取记录并停止重试的信号。
OpenAI SDK 本身就会对 408、409、429 或 5xx 重试两次,并遵循 x-should-retry: false,所以你可能根本不需要自己写循环。
下一步
- 错误列出了每个 code 及其状态码。
- 提交工单时请引用请求的
id:每个请求都有可引用的 ID。