Skip to Content
API 参考错误码与重试

错误码与重试

ttttt.ai 网关使用统一的错误信封,无论上游返回什么格式,对外都规整成下面这种结构:

{ "error": { "message": "Human readable description", "type": "invalid_request_error", "code": 400 } }

type 是机器可读的语义码,code 与 HTTP 状态码一致,message 是给人看的解释(可能包含上游原文)。

HTTP 状态码总表

HTTPtype含义重试
400invalid_request_error请求体不合法(缺字段、类型错、参数越界)❌ 不要重试
401unauthorized密钥无效 / 已停用 / 已过期
402insufficient_balance组织余额不足 / 订阅额度耗尽且未开溢出❌(先充值/调订阅)
403forbidden密钥 scope 拒绝 / 模型未开放 / 组织被冻结
404not_found模型 ID 不存在 / 路径写错
409conflict资源冲突(罕见,主要在控制台 API)
429rate_limit_exceeded触发了密钥 RPM/TPM 或上游限速✅ 指数退避
499client_closed客户端中断(流式专用)业务决定
500server_error网关内部错误✅ 短退避
502bad_gateway上游不可达✅ 短退避
503service_unavailable上游或网关过载✅ 短退避
504gateway_timeout上游超时✅ 短退避

401 unauthorized

最常见原因 + 排查:

现象检查
Invalid API key控制台密钥列表里这把 Key 是否还存在 / 状态是否 active
Key expired是否到了 expiresAt;过期密钥不会自动续
Missing API key请求是不是真的发出了 Authorization / x-api-key header(curl 加 -v 看实际发出的 header)
Organization deactivated组织是否进入冻结期;联系工单

402 insufficient_balance

{ "error": { "message": "Insufficient balance: required $0.0145, available $0.0021", "type": "insufficient_balance", "code": 402 } }

含义:本次预估费用 + 安全冗余 > 当前可用余额(三桶合计)。

处理:

  • 充值:控制台 → 账单 → 充值
  • 长期可通过 低余额告警 提前预警,详见 余额告警

403 forbidden

{ "error": { "message": "Model 'claude-opus-4-7' is not allowed for this API key", "type": "forbidden", "code": 403 } }

排查:

  1. 该模型当前是否在公开列表里(模型清单
  2. 密钥 modelScope 是否限制了模型

429 rate_limit_exceeded

{ "error": { "message": "Rate limit exceeded: 60 requests per minute", "type": "rate_limit_exceeded", "code": 429 } }

响应可能附带:

Retry-After: 12

客户端应当遵循 Retry-After(秒);没有该 header 时按指数退避(1s → 2s → 4s → 8s,最多 5 次)。

被限速的常见原因:

  • 密钥本身设置了 rateLimitRpm / rateLimitTpm
  • 上游供应商对你的请求模式触发了限速(突发并发太高)

5xx 系列

500 / 502 / 503 / 504 都属于”应当短期重试”的临时错误:

// 推荐重试策略 async function withRetry<T>(fn: () => Promise<T>, maxAttempts = 3): Promise<T> { let attempt = 0; while (true) { try { return await fn(); } catch (e: any) { attempt++; const status = e.status ?? e.response?.status; const retriable = status === 429 || (status >= 500 && status < 600); if (!retriable || attempt >= maxAttempts) throw e; const delayMs = Math.min(2 ** attempt * 500, 8000) + Math.random() * 200; await new Promise(r => setTimeout(r, delayMs)); } } }

平台自身已经做了一层渠道降级——上游 5xx 时网关会切换到同型号的备用渠道再重试一次,所以如果你拿到了 5xx,意味着所有备用都失败了。建议前端重试 ≤ 3 次,再多就该报错让人介入。

错误响应里的 Request ID

每个错误响应同样带 X-Request-ID: req-... header,把它存到你的日志里,提工单时附上 ID 运营可以一秒定位。

curl -i https://api.ttttt.ai/v1/chat/completions ... # < HTTP/1.1 502 Bad Gateway # < X-Request-ID: req-018ff3a2-9bca-7f29-bd0a-1c4b3d8e0c8e

失败请求的计费

状态码是否扣费
2xx按真实 usage 扣
4xx(客户端错误)不扣费
5xx(最终失败)不扣费
429(限速)不扣费
流式中断按已估算的 output 部分扣

详见 计费模型 → 失败请求的计费

与官方 SDK 的兼容

OpenAI / Anthropic SDK 的内置重试逻辑(max_retries=2 等)直接可用——它们会按 HTTP 状态码 + Retry-After 处理 429/5xx,与 ttttt.ai 网关行为一致。

不需要自己实现的话,把 SDK 的 max_retries 调到 2-3 即可:

client = OpenAI(api_key="owo-...", base_url="https://api.ttttt.ai/v1", max_retries=3)
Last updated on