可以据此处理的错误

· Hyperouter · 阅读约 5 分钟

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 指明字段。修正请求。不要原样重试。
401API 密钥缺失或无效。检查密钥。
402insufficient_credits:你的余额或密钥的额度上限,不足以覆盖该请求可能产生的最高费用。充值,或提高密钥的额度上限。
403账户已被暂停,或某个功能或模型未对其开通。提交支持工单。
404id 不存在、id 属于其他账户,或视频已超过其 expires_at。不要重试。
409当前状态不允许该调用,或 Idempotency-Key 冲突。见安全重试。
429所有服务商均已满载,或你的账户超出速率限制。等待 Retry-After 给出的时间,然后重试。
500, 501Hyperouter 自身出错,或接口尚未实现。不计费。谨慎重试 500,见下文。
503服务商出错或未在时限内响应。重试,见下文。

你会遇到的三个 code

同一类还包括 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:

对于聊天,已接受请求的服务商可能正在运行它。如果它随后不再响应,该请求不会被转发给其他服务商:响应为 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

对于聊天请求,如果首次尝试确实已经运行,重试会收到 409 idempotency_key_replayed。这就是该用 GET /v1/generations/{id} 读取记录并停止重试的信号。

OpenAI SDK 本身就会对 408、409、429 或 5xx 重试两次,并遵循 x-should-retry: false,所以你可能根本不需要自己写循环。

下一步