HTTP 语义与错误载荷
本文档描述 Routic API 返回的 HTTP 状态码和错误载荷。业务 API 错误与网关(LLM)错误可能在 HTTP 状态和 JSON 结构上有所不同——请以实际响应为准并做相应处理。
错误响应格式
所有错误返回一致的 JSON 格式:
{
"error": {
"message": "提供的 API 密钥无效。",
"type": "authentication_error",
"code": "invalid_api_key",
"param": null,
"request_id": "req_abc123def456"
}
}
错误字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
message | string | 人类可读的错误描述。 |
type | string | 错误类别(如 authentication_error、invalid_request_error、rate_limit_error)。 |
code | string | 机器可读的错误码,用于程序化处理。 |
param | string / null | 导致错误的请求参数(如适用)。 |
request_id | string | 唯一请求标识,用于调试和支持。始终记录此值。 |
HTTP 状态码
200 OK
请求成功。响应体包含请求的数据。
400 Bad Request
请求格式错误或缺少必填参数。
| Code | Type | 消息示例 | 原因与解决方案 |
|---|---|---|---|
invalid_request_error | invalid_request_error | "无效请求体:'messages' 是必填项。" | 请求体缺少必填字段或格式无效。检查请求参数并重试。 |
invalid_model | invalid_request_error | "模型 'gpt-999' 不可用。" | 指定的模型不在你的目录中或不存在。检查模型目录。 |
invalid_message_format | invalid_request_error | "每条消息必须有 'role' 和 'content' 字段。" | messages 数组结构无效。每条消息必须有 role 和 content。 |
重试:不要在未修复请求的情况下重试。
401 Unauthorized
认证失败。
| Code | Type | 消息示例 | 原因与解决方案 |
|---|---|---|---|
invalid_api_key | authentication_error | "提供的 API 密钥无效。" | API 密钥格式错误或不存在。检查密钥格式(sk-*)。 |
expired_api_key | authentication_error | "你的 API 密钥已过期。" | 密钥已到达预算期限或被吊销。在控制台生成新密钥。 |
missing_api_key | authentication_error | "未提供 API 密钥。请包含 Authorization 头。" | 缺少 Authorization: Bearer 头。添加到请求中。 |
重试:不要重试。先修复认证问题。
402 Payment Required
余额不足。
| Code | Type | 消息示例 | 原因与解决方案 |
|---|---|---|---|
insufficient_balance | insufficient_quota | "账户余额不足,请充值。" | 账户余额为零或负数。充值或等待账单周期重置。 |
budget_exceeded | insufficient_quota | "该密钥的月度预算已超出。" | 密钥已超出配置的预算。在控制台增加预算或创建新密钥。 |
重试:不要重试。先充值或调整预算。
422 Unprocessable Entity
请求语法有效但语义无效。
| Code | Type | 消息示例 | 原因与解决方案 |
|---|---|---|---|
unsupported_feature | invalid_request_error | "该模型不支持工具调用。" | 指定的功能(如 tools、response_format)不被所选模型支持。检查能力矩阵。 |
parameter_conflict | invalid_request_error | "'thinking' 参数需要推理模型。" | 参数组合无效(如在非推理模型上使用 thinking)。 |
重试:不要重试。先修复参数问题。
429 Too Many Requests
超出速率限制。
| Code | Type | 消息示例 | 原因与解决方案 |
|---|---|---|---|
rate_limit_exceeded | rate_limit_error | "RPM 限制已超出,请 5 秒后重试。" | 密钥已超出每分钟请求数限制。等待并使用指数退避重试。 |
token_rate_limit_exceeded | rate_limit_error | "TPM 限制已超出,请 10 秒后重试。" | 密钥已超出每分钟 token 数限制。等待并使用指数退避重试。 |
重试:是,使用指数退避。见下方重试策略。
500 Internal Server Error
服务器端发生未预期的错误。
| Code | Type | 消息示例 | 原因与解决方案 |
|---|---|---|---|
internal_error | api_error | "发生意外错误,请重试。" | 服务器端问题。记录 request_id 并重试。如持续发生,联系支持。 |
重试:是,使用指数退避。
503 Service Unavailable
模型提供商不可用。
| Code | Type | 消息示例 | 原因与解决方案 |
|---|---|---|---|
upstream_unavailable | api_error | "上游提供商暂时不可用,请重试。" | 模型提供商遇到问题。Routic 可能自动路由到备用。延迟后重试。 |
重试:是,延迟后重试(如 30 秒)。网关可能自动用备用模型重试。
重试策略
何时重试
| 状态码 | 重试? | 策略 |
|---|---|---|
| 400 | 否 | 修复请求并重发。 |
| 401 | 否 | 修复 API 密钥并重发。 |
| 402 | 否 | 充值或调整预算。 |
| 422 | 否 | 修复参数问题并重发。 |
| 429 | 是 | 带抖动的指数退避。 |
| 500 | 是 | 带抖动的指数退避。 |
| 503 | 是 | 退避,延迟更长(如 30 秒)。 |
带抖动的指数退避
延迟 = min(基础延迟 * 2^尝试次数 + 随机抖动, 最大延迟)
推荐值:
| 参数 | 值 |
|---|---|
| 基础延迟 | 1 秒 |
| 最大重试 | 3 |
| 最大延迟 | 60 秒 |
| 抖动范围 | 0–500 毫秒 |
Python 示例
import time
import random
def retry_with_backoff(func, max_retries=3, base_delay=1, max_delay=60):
for attempt in range(max_retries):
try:
return func()
except Exception as e:
if attempt == max_retries - 1:
raise
delay = min(base_delay * (2 ** attempt) + random.uniform(0, 0.5), max_delay)
print(f"第 {attempt + 1} 次尝试失败。{delay:.1f} 秒后重试...")
time.sleep(delay)
网关与提供商错误
Routic 作为网关代理。错误可能来自:
| 来源 | 说明 | HTTP 状态 |
|---|---|---|
| Routic | 认证、计费、速率限制、请求校验 | 400、401、402、429 |
| 网关 | 路由、回退、格式归一化 | 422、500 |
| 模型提供商 | 模型特定错误、内容政策违规 | 400、422、500、503 |
所有情况下,错误响应格式都归一化为上文描述的结构。request_id 可用于跨层追踪问题。
速率限制行为
- 速率限制按 每个 API Key 应用,不是按账户。
- 触发速率限制时,网关 不会 排队请求——立即以 429 拒绝。
- 429 响应可能包含
Retry-After头,指示等待的秒数。 - 每个密钥的速率限制可配置。默认值:100 RPM、10,000 TPM。