跳到主要内容
AIRouter
关于我们
登录免费注册
AIRouter

统一接入全球大模型,按量计费,无最低消费。

support@airouter.hk

产品

  • 模型广场
  • 计费口径
  • 免费开始

开发者

  • 文档
  • 快速开始
  • 服务状态
  • API Key 管理

公司

  • 关于我们
  • 服务条款
  • 隐私政策

支持

  • 帮助中心
  • 登录
  • 进入控制台

© 2026 AIRouter. 保留所有权利。AIRouter 是模型聚合与路由服务,模型能力由各供应商提供。

所有系统运行正常

开发者文档

文档目录

入门

  • 平台介绍
  • 快速开始
  • 鉴权

API 参考

  • 对话补全
  • 图像生成
  • 文本向量化
  • 重排
  • 视频生成
  • 模型与路由

平台机制

  • 计费口径
  • 错误码与排查

入门

  • 平台介绍
  • 快速开始
  • 鉴权

API 参考

  • 对话补全
  • 图像生成
  • 文本向量化
  • 重排
  • 视频生成
  • 模型与路由

平台机制

  • 计费口径
  • 错误码与排查

错误码与排查

错误封装格式、全部错误码,以及各类错误的重试策略。

错误封装#

错误封装按入站协议给出。OpenAI 协议(/v1/chat/completions 等):

json
{  "error": {    "message": "账户余额不足",    "type": "insufficient_quota",    "code": "insufficient_balance",    "param": "model"  },  "request_id": "01JQ8Z3P9K7V2M4X6B8N0C1D3E"}

Anthropic 协议(/v1/messages):

json
{  "type": "error",  "error": {    "type": "invalid_request_error",    "message": "账户余额不足"  },  "request_id": "01JQ8Z3P9K7V2M4X6B8N0C1D3E"}

火山方舟视频协议(/api/v3/contents/generations/tasks)成功时响应形状跟方舟一致;失败仍走网关统一错误处理,排查请带上 X-Request-Id / request_id。状态字为 queued / running / succeeded / failed / cancelled,与 OpenAI 视频的 in_progress / completed 不同,见 视频生成 · 轮询。

  • code 是机器可读的稳定标识,请以它为分支依据。注意 Anthropic 协议里没有 code——那条端点上只能按 HTTP 状态与 error.type 分支。
  • type 是为兼容 SDK 的重试逻辑而映射的官方取值,不要用它区分具体原因。
  • param 只在参数类错误上出现,给出出错的字段名。
  • request_id 与响应头 X-Request-Id 一致,报障时提供它即可直达明细。
  • 更细的结构化信息(命中的限流键、被剔除的线路、上游状态码等)不在响应体里,按 request_id 去 调用日志 查。

错误码总表#

鉴权与令牌

codeHTTP含义
missing_credential401没带 API Key。
invalid_credential401Key 无效(也包括拿控制台 JWT 来调网关)。
key_disabled403该 Key 已被禁用。
key_expired403该 Key 已过期。
key_quota_exhausted402该 Key 的额度用尽。
ip_not_allowed403来源 IP 不在该 Key 的白名单内。
model_not_allowed403该 Key 不允许访问这个模型。

请求与参数

codeHTTP含义
invalid_request400参数不合法,出错字段名在 error.param。
unsupported_feature400所选模型或线路不支持该功能。
context_too_long400输入超出上下文窗口。
model_not_found404模型不存在或已下线。
request_too_large413请求体过大,通常是内联 base64 图片太大。

幂等键(带了 Idempotency-Key 才会出现)

codeHTTP含义
idempotency_key_in_use409同一个键的上一次请求尚未结束。请退避数秒后使用同一个键重试,此时不会产生第二次扣费。
idempotency_key_replayed409同一个键已经执行过一次,本次没有再调上游也没有再计费。原请求 ID 在 error.details 里,正文去调用日志取。
idempotency_key_reused400同一个键本次的请求内容发生了变化。请改用新的键——内容变更即视为一个新请求。

计费与额度

codeHTTP含义
insufficient_balance402余额不足。所需与可用金额在调用日志里。
billing_unavailable503计费服务暂时不可用,可退避重试。
quota_lease_exhausted503区域额度租约暂时不可用,可退避重试。

限流

codeHTTP含义
rate_limited429命中 RPM / TPM 限速。
concurrency_limited429并发数超限,流式长连接会长时间占用并发位。

路由与上游

codeHTTP含义
no_candidate503过滤后没有可用线路。最常见的原因是 provider 写得太严。
all_candidates_failed502全部候选线路均已尝试且仍然失败,网关已完成内部重试。
upstream_timeout504上游超时。具体是连接、首字节还是总时长超时,请查阅调用日志中的路由轨迹。
upstream_error502上游返回错误。上游状态码在调用日志里。
upstream_rate_limited429上游限流,网关已尝试换线。
upstream_overloaded503上游过载,高峰期常见。
circuit_open503目标线路熔断中,探测成功后自动放回。
empty_completion502上游 200 但零产出。本次不计费。
content_filtered400内容被审核拦截。

客户端与内部

codeHTTP含义
client_closed_request499客户端主动断开。
internal_error500平台内部错误,请带 request_id 报障。
not_found404资源不存在。
conflict409状态冲突,例如取消一个已经在跑的视频任务。
region_denied403该请求不允许在当前区域处理。

重试策略#

不应对所有错误采用统一的重试策略。下列三档按「重发整个请求是否有意义」划分。

档位错误码
不要重试(配置或请求本身的问题)missing_credential invalid_credential key_disabled key_expired key_quota_exhausted ip_not_allowed model_not_allowed invalid_request unsupported_feature context_too_long model_not_found request_too_large insufficient_balance content_filtered no_candidate
尊重 Retry-After 后重试rate_limited concurrency_limited upstream_rate_limited
指数退避重试(初始 1s,×2,最多 3 次,加 ±20% 抖动)billing_unavailable quota_lease_exhausted upstream_timeout upstream_error upstream_overloaded circuit_open empty_completion
已产生输出的请求不应整体重发——已产出部分已经计费,重发会造成重复支出。empty_completion 则相反:该错误不计费,直接重试没有额外成本。

Python:只重试该重试的,并且尊重 Retry-After

python
import random, time, requests
# 429 是限流,5xx 是平台或上游的临时故障:这两类值得重试。# 4xx 里的其余错误是请求本身有问题,重试多少次结果都一样。RETRIABLE = {429, 500, 502, 503, 504}

def call_with_retry(url, body, headers, attempts=4):    for attempt in range(attempts):        response = requests.post(url, json=body, headers=headers, timeout=120)        if response.status_code not in RETRIABLE:            return response        if attempt == attempts - 1:            break
        # 服务端说了等多久就等多久,没说才自己退避。        # 随机抖动是为了避免一批客户端在同一毫秒一起重来。        wait = response.headers.get("Retry-After")        delay = float(wait) if wait else (2**attempt) + random.random()        time.sleep(delay)
    return response

Node.js:同样的重试策略

javascript
// 429 是限流,5xx 是平台或上游的临时故障:这两类值得重试。// 4xx 里的其余错误是请求本身有问题,重试多少次结果都一样。const RETRIABLE = new Set([429, 500, 502, 503, 504]);
async function callWithRetry(url, body, headers, attempts = 4) {  let response;
  for (let attempt = 0; attempt < attempts; attempt += 1) {    response = await fetch(url, {      method: 'POST',      headers: { ...headers, 'Content-Type': 'application/json' },      body: JSON.stringify(body),    });    if (!RETRIABLE.has(response.status)) return response;    if (attempt === attempts - 1) break;
    // 服务端给了 Retry-After 就按它等,没给才自己退避;    // 随机抖动用于避免一批客户端在同一时刻一起重来。    const retryAfter = response.headers.get('Retry-After');    const delay = retryAfter ? Number(retryAfter) : 2 ** attempt + Math.random();    await new Promise((resolve) => setTimeout(resolve, delay * 1000));  }
  return response;}

Java:只重试该重试的,并且尊重 Retry-After

java
import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import java.util.Set;
// 429 是限流,5xx 是平台或上游的临时故障:这两类值得重试。// 4xx 里的其余错误是请求本身有问题,重试多少次结果都一样。static final Set<Integer> RETRIABLE = Set.of(429, 500, 502, 503, 504);
static HttpResponse<String> callWithRetry(HttpRequest request, int attempts) throws Exception {    HttpClient http = HttpClient.newHttpClient();    HttpResponse<String> response = null;
    for (int attempt = 0; attempt < attempts; attempt++) {        response = http.send(request, HttpResponse.BodyHandlers.ofString());        if (!RETRIABLE.contains(response.statusCode())) {            return response;        }        if (attempt == attempts - 1) {            break;        }
        // 服务端给了 Retry-After 就按它等,没给才自己退避;        // 随机抖动用于避免一批客户端在同一时刻一起重来。        double delay = response.headers().firstValue("Retry-After")            .map(Double::parseDouble)            .orElse(Math.pow(2, attempt) + Math.random());        Thread.sleep((long) (delay * 1000));    }
    return response;}

限流#

限流有 RPM、TPM、并发三个维度。RPM 与 TPM 按 API Key 与账户两级判定,并发按账户判定。

  • RPM 与 TPM 超限都返回 rate_limited(429),并发占满返回 concurrency_limited(429)。
  • TPM 按估算的输入 token 计量;真实用量要在上游返回后才可知。
  • 429 一律带 Retry-After(秒,最小值为 1)。请按该值退避;不遵守退避会延长被限流的时间。
  • 流式长连接会持续占用并发位直到流结束,因此并发限额往往比预期更早触发。

失败请求的排查步骤#

  1. 1

    拿到 request_id

    响应头 X-Request-Id,或错误体里与 error 同级的 request_id,两者一致。

  2. 2

    在调用日志里搜它

    调用日志 支持按 request_id 精确查,能看到请求参数摘要、路由轨迹与计费明细。

  3. 3

    看路由轨迹

    路由轨迹列出每次尝试所用的线路、上游状态码与耗时。all_candidates_failed 基本都能据此定位到失败环节。

  4. 4

    仍无法定位时提交工单

    携带 request_id 提交 工单 即可,无需附上完整请求体。

两个高频误报
将视频模型发送到 /v1/chat/completions 会返回 400,并提示改用 POST /v1/videos。no_candidate 绝大多数并非「目录中没有该模型」,而是 provider 的约束把所有线路都筛除了——请查阅 调用日志 中的路由轨迹,其中记录了每条线路被哪个条件筛除。
上一篇计费口径

本页目录

  • 错误封装
  • 错误码总表
  • 重试策略
  • 限流
  • 失败请求的排查步骤