LLM API 错误码:逐个 解释,逐个 修复
把 HTTP 状态码、错误 type/code 和流的终止事件一起看。400 可能是模型端点用错,402 是计费限制,429 是限流;HTTP 200 的流也可能以 response.failed 结束。先保留响应里的 request_id:在调用任何模型之前就被拒绝的请求(例如端点用错的 400、402、Key 或账户限额触发的 429)在控制台 → 日志里没有记录;到达模型之后才失败的请求通常会在那里留下最终请求记录,支持团队可据此关联中间尝试(在输出任何内容之前就失败的流式请求可能没有)。
速查表
每个状态码第一步先查什么:
| 状态码 | 含义 | 第一步查什么 |
|---|---|---|
| 400 | 请求无效,或用错了该模型的端点 | 报错消息会点明原因:网关不提供的 Responses 特性(background、image_generation)、response_format 外层结构不完整,或者该模型必须走另一条路径——model '<id>' must be called via /v1/messages or /v1/chat/completions。 |
| 400 CONTENT_FILTERED | 调用模型之前被内容策略拦截 | 图像或视频生成的提示词未通过内容审核时,消息为 prompt rejected by content moderation。被拦截的请求不会调用任何模型,也不计费。不要自动重试;如果认为是误判,带上 request_id 联系支持。内容审核本身不可用时,图像与视频请求会以 502 MODERATION_UNAVAILABLE 拒绝,而不是跳过审核直接生成,稍后重试即可。 |
| 401 | API Key 无效或缺失 | Authorization 头需要以 Bearer sk-… 携带 Key——检查拼写、首尾空格,以及运行客户端的那个 shell 里环境变量是否为空。 |
| 402 | 可用资金或 Key 预算不足 | 先读错误消息及已有的 X-Billing-Funds-Reason 响应头。可能是在途请求预留了资金,也可能是 Key 的 maxSpend 已触顶;钱包充值不会提高 Key 消费上限。 |
| 403 | 拒绝访问 | 一般不是 Key 的问题:已吊销、已过期或不存在的 Key 返回的是 401,不是 403。请保留完整错误消息和 request_id 交给支持排查,并核对客户端用的 base URL 是否正确。 |
| 404 | 路径或模型不存在 | 请求路径或模型 ID 不存在。从 /models 页复制精确的模型 ID——ID 区分大小写。 |
| 429 | 请求频率超限 | 错误码为 RATE_LIMIT_EXCEEDED(每分钟请求数)或 TOKEN_QUOTA_EXCEEDED(每分钟 token 数)。日志里有记录的 429,是网关重试之后上游仍返回的。网关对这把 Key、这个账户或账户在单个模型上的限额会在调用任何模型之前拒绝请求:这类 429 在控制台 → 日志里没有记录,X-RateLimit-Scope 为 api_key、subject 或 subject_model,并按同一频率反复出现;X-RateLimit-Scope 为 upstream_provider 时指向模型厂商的限制。流式请求在输出任何内容之前就遇到上游限流时也可能没有记录,所以没有记录、但突发出现又会自行消散的 429,同样指向上游。按 Retry-After 退避。Key 与账户限额按平台默认值生效,可申请调高:发邮件到 support@router.one,写明账户 ID、Key 和预计峰值流量。 |
| 499 | 客户端取消了请求 | 响应还没结束,客户端就关闭了连接——停止按钮、SDK 超时或网络断开。它不是上游故障,也不会故障转移。/v1/chat/completions 与 /v1/responses 上被取消的流,按上游报告的用量计费;已有输出却没拿到最终 usage 时按估算计费(截至 2026-10-13),在输出任何内容之前就关闭、且没拿到任何 usage 的流不计费(详见流式指南)。/v1/chat/completions、/v1/responses 与 /v1/messages 上 stream: false 的调用,在客户端断开后仍会继续执行(截至 2026-10-09):模型跑完,就按它报告的用量计费,Trace 显示这部分 Token;模型失败或超出网关的非流式时间窗,则不计费。 |
| 500 | 服务器内部错误 | 瞬时故障。退避重试;持续出现请联系支持。 |
| 502 | 上游响应失败 | 查看错误正文或流内事件并保留 request_id。仅凭 502 无法判断是否所有可用线路都失败,也不能排除内层错误是限流。 |
| 503 | 服务或模型线路暂不可用 | 核对目录与错误 code。PROVIDER_UNAVAILABLE 可能表示当前没有可用线路,不能据此认定模型 ID 不存在;瞬时故障可采用有上限的退避重试。 |
| 504 | 等待上游时超时 | Router One 会把部分 timeout 或 deadline 错误归一化成 504 PROVIDER_UNAVAILABLE,不能据此认定上游实际返回 HTTP 504。保留错误消息、端点、request_id 与时间,先区分等待响应头超时和流中途断开。 |
| 529 | 上游过载 | 模型上游饱和。若错误符合重试条件,且另一个健康供应商提供同一精确模型,Router One 可以尝试该路由;否则请采用有上限的退避重试。 |
| 超时 | 未在时限内响应 | 先识别谁停止了等待:SDK、代理、网关还是上游。客户端超时可能没有任何 HTTP 错误响应;把它的 deadline 设置与响应头、流内事件、最终请求记录对照。 |
还有 余额却 返回 402:钱包 资金 还是 Key 上限?
当 402 响应包含 X-Billing-Funds-Reason 时,结合其值和错误消息区分可用资金、临时预留与 Key 限额。诊断以实际返回的响应为准;客户端未展示这些字段时,保留消息和 request_id 交给支持团队,不要只看控制台余额推断原因。
| 实际返回的 reason | 下一步 |
|---|---|
| wallet_reserved | 部分钱包资金被在途请求预留。等待结算后再检查可用金额,最终费用会影响释放量。 |
| key_reserved | 在途请求占用了该 Key 的部分预算。等待结算,或到控制台 → API 密钥调整 maxSpend;钱包充值不会解除这个限制。 |
| key_cap | 该 Key 的可用消费预算不足。按需提高或清除 maxSpend,充值不会改变这个上限。 |
| wallet_insufficient | 可用钱包资金低于本次准入所需的初始预留。核对请求大小与可用金额,再减少输入或充值。 |
- 若响应包含 X-Billing-Available-Balance-USD 与 X-Billing-Reserved-Balance-USD,可用它们区分钱包可用金额和已预留金额;X-Billing-Minimum-Required-USD 表示准入所需的最低初始预留,不是最终账单。
- 配置了上限的 Key 还可能返回 X-Billing-Available-Key-Budget-USD 与 X-Billing-Reserved-Key-Budget-USD,描述独立的 Key 预算。预留释放不保证下一次一定成功,最终扣费和其他并发请求仍会改变可用金额。
为什么 HTTP 200 后还会 出现 response.failed?
SSE 连接的 HTTP 200 只表示流已开始,不代表生成完成。Responses 协议需要读取 response.failed 内的 response.error,也要处理顶层 error 事件。rate_limit_error 或 Too many pending requests 表示限流;response.incomplete 则需检查 incomplete_details。保留已收到的部分输出,终止事件前意外断连应视为未完成。一旦内容已发送给客户端,就不能假定网关还能换线路重放请求。
event: response.failed
data: {"type":"response.failed","response":{"status":"failed","error":{"type":"rate_limit_error","message":"Too many pending requests, please retry later"}}}504 或 SDK 超时:到底哪 一层 停止了 等待?
超时只说明某个等待上限已到,并不是完整归因。先确认是否收到 HTTP 响应、是否已有模型输出到达客户端。保留端点、模型 ID、可获取的 request_id、时间与时区、实际耗时和原始错误文字,不要附带 API Key。支持团队可以关联最终请求与中间尝试;仅凭状态码不能确认是哪一段网络出了问题。
| 观察到的证据 | 能证明什么 | 下一步查什么 |
|---|---|---|
| SDK 超时,未收到 HTTP 响应 | 客户端停止了等待,不能据此认定上游返回了 HTTP 错误。 | 检查建连、读取和总时长限制,以及本机网络或代理错误。若请求到达网关后连接被关闭,最终日志可能记录为客户端取消。stream: false 的调用即使客户端已超时,模型仍可能跑完并计费(见 499);SDK 超时后自动发出的重试是另一个独立的请求,模型跑完也会单独计费(截至 2026-10-09)。 |
| 关联的上游尝试中出现 Client.Timeout exceeded while awaiting headers | 网关的 HTTP 客户端等到超时时,仍未收到上游响应头;归一化的 504 不是上游实际返回 HTTP 504 的证据。 | 交由支持核对原始尝试与该路径的超时设置。不要套用 SDK 超时,也不要假定所有端点采用相同限制。 |
| HTTP 200,随后出现 error 事件或意外 EOF | SSE 传输已打开,但生成可能失败或尚未完成。 | 检查终止事件,保留部分输出,重试前参照流式指南;不能假定已交付的流会在另一条线路自动重新开始。 |
| 非流式调用返回 HTTP 200,但 JSON 正文是错误 | 这次调用耗时足够长,网关的保活已经开始(约 25 秒):HTTP 200 已提交,并写入了 JSON 前导空白,此后的失败无法再改状态码。 | 先判断是不是错误,再读结果:/v1/chat/completions 与 /v1/responses 上是顶层 error 对象,/v1/messages 上是 "type": "error" 加 error 对象(Anthropic 错误外层结构);按其中的 type 与 code 分类,处理方式与非 200 响应相同。截至 2026-10-09,适用于 /v1/chat/completions、/v1/responses 与 /v1/messages 上耗时较长的 stream: false 调用;需要几分钟的生成请用 stream: true。 |
- 指定模型的原生 /v1/responses 流允许长等待,这一策略不能泛化到所有协议和非流式请求。客户端、代理和上游自己的限制仍然存在,model:auto 也保留候选时间预算。
- 只有真正收到上游 HTTP 响应,才能确认它的状态码。网关归一化错误、某个候选的失败与整次请求的最终结果需要分别看;符合条件的 fallback 之后,整次请求仍可能成功。
先看 trace,再动手改
Router One 的客户侧请求记录展示模型、Token、结算费用、总耗时,以及产生过输出的流式请求的首字延迟(TTFT),还有状态。控制台 → 日志支持按模型、API Key、状态和日期过滤,便于判断错误是偶发还是规律。这里不展示供应商或中间尝试;如需排查线路和故障转移链,请把 request_id 提供给支持团队。等待定价的请求可能暂未出现在列表中,查不到记录不能证明零计费。
Claude Code 里的 报错
Claude Code 走 Anthropic 兼容端点 https://api.router.one——注意:结尾没有 /v1。Claude Code 里的 401/403 多数来自环境变量:没设、设在了另一个 shell、或被之前登录官方服务的会话覆盖。接网关时 Claude Code 认的是 ANTHROPIC_AUTH_TOKEN;ANTHROPIC_API_KEY 不需要设置,设了反而会多弹一次授权确认。写着 model '<id>' must be called via /v1/chat/completions 的 400 则是另一回事:你配置的模型 ID 不在 Anthropic 兼容端点上服务——换成详情页列出 POST /v1/messages 的模型(当前目录中的 Claude 系列模型与 DeepSeek ID),或者把该模型改走 /v1/chat/completions。Claude Code 自己的登录类报错(Not logged in · Please run /login、Invalid API key、Invalid auth token · Fix external auth token、Login expired · Please run /login)各自对应的修复见 Claude Code 401 / 403 排查页(https://router.one/zh/claude-code-403)。属于鉴权问题的话,按下面设好并重启终端:
export ANTHROPIC_BASE_URL=https://api.router.one export ANTHROPIC_AUTH_TOKEN=sk-your-router-one-key unset ANTHROPIC_API_KEY
Codex CLI 里的 报错
Codex CLI 使用 Responses API 协议,而许多 OpenAI 兼容中转并没有实现它——这就是 Codex 对着它们报 404 的原因。Router One 原生支持 wire_api = "responses"。在 ~/.codex/config.toml 里写顶层的 model = "gpt-5.6-sol"(不写 model 时,Codex 0.159.1 起会用内置默认模型 gpt-6.1-sol),再配置 base_url = "https://api.router.one/v1"、wire_api = "responses"、env_key = "ROUTER_ONE_API_KEY",并导出 ROUTER_ONE_API_KEY 环境变量。自定义 provider 下 Codex 不读 OPENAI_BASE_URL / OPENAI_API_KEY,而 requires_openai_auth 会让它完全忽略 env_key。若收到提到 background 或 image_generation 的 400 invalid_request,是网关拒绝了两项不支持的 Responses 特性:去掉 background = true,图片生成改走 /v1/images/generations 而不是 image_generation 工具;hosted 工具与 previous_response_id 在原生走 Responses 协议的模型上可用。写着 model '<id>' must be called via /v1/messages or /v1/chat/completions 的 400 是另一回事:Claude 家族的模型 ID 打到了 Responses 端点,而 Claude 不在这里服务——把 Codex 换成 当前目录中的 GPT 系列模型,要用 Claude 就走 Claude Code。另有两个流式报错由 Codex 自己抛出(报错文字来自 Codex,不是网关返回的错误体):stream disconnected before completion: idle timeout waiting for SSE 表示在 Codex 的 stream_idle_timeout_ms(默认 300000 毫秒)内没收到任何 SSE 事件;stream disconnected before completion: stream closed before response.completed 表示连接在最终事件之前就关闭了。可按时间和模型在控制台 → 日志里找到这次请求。GPT 模型长时间推理时,在自定义的 [model_providers.<id>] 块里调大 stream_idle_timeout_ms,并检查代理或网络;网关不会因为空闲而结束原生 Responses 流。完整步骤(含这两个流式报错)见 Codex Responses API 页。
常见 问题
Key 看起来没 问题,为什么 还是 401?
三个最常见原因:环境变量设在了另一个 shell(或另一个 profile 文件)里,而不是运行客户端的那个会话;粘贴 Key 时带了尾部空格或换行;客户端读取的变量名和你设置的不一致。在同一会话里检查预期变量是否已设置即可,不要打印 Key 或把它带进截图。
哪些 Authorization 头的 写法最 容易 导致 401?
头的值必须严格是 Bearer、一个空格、再接 Key。最常见的错误形态:scheme 写了两遍(Bearer Bearer sk-…),因为客户端会自动加前缀而环境变量里已经带了;把粘贴时连带的引号也当成了 Key 的一部分;Key 中间混入了换行或空格;Authorization 头存在但值为空,或者只发了空的 x-api-key;把控制台的登录会话 token(JWT)当成 API Key 发了;以及不带 scheme 直接写 Authorization: sk-…。要修的是头的值而不是 Key——被这样改坏的有效 Key 在查库之前就会被拒绝。
402 和 429 有什么 区别?
402 关乎本次请求可用的资金或 Key 预算,也可能涉及在途请求预留;充值前先读已有的 X-Billing-Funds-Reason 诊断。429 关乎请求或 token 限额,也可能是上游限制。先看响应 code 和 message,再在有记录时到控制台 → 日志关联请求。
504 PROVIDER_UNAVAILABLE 能证明 上游 返回了 HTTP 504 吗?
不能。Router One 可能把 timeout 或 deadline 错误归一化为该状态,即使根本没有收到上游响应头。SDK 在客户端超时又是另一种情况,可能没有 HTTP 响应。保留原始错误消息和可获取的 request_id,让支持分别核对原始上游尝试、是否发生 fallback,以及整次请求的最终结果。
遇到 5xx 需要 自己写 重试 逻辑吗?
符合条件的 5xx 或超时,在另一个健康供应商提供同一精确模型时,可以由 Router One 重试。并非每个错误都保证重试,因此应用收到失败时仍应采用有上限的退避策略;若问题持续,请联系支持。
返回了 200,但 message 是空的,要不要 重试?
先区分已完成的 Chat Completions 回复和失败的流。已完成且 usage 非零的回复可能因推理或拒答而没有正文;重试前先看 finish_reason、refusal 和 usage,因为重复调用可能再次收费。HTTP 200 伴随 response.failed、error 事件或流中断则是另一种情况:检查终止事件并保留 request_id。完全没有 usage 的空上游回复按上游错误处理。
response.failed 里出现 Too many pending requests,是 Key 无效吗?
不是。内层 rate_limit_error 表示限流,不是鉴权失败。先降低并发,再做有上限的退避重试。向支持提供 request_id、模型 ID、请求端点、时间与时区、错误 type/code,不要附 API Key。仅凭这条错误无法证明尝试了哪条线路,也不能判断故障转移是否执行。
同一个 Key,curl 能用,Claude Code 却 403?
curl 走的是 OpenAI 兼容端点;Claude Code 需要不带 /v1 后缀的 Anthropic 兼容 base URL,并设置 ANTHROPIC_AUTH_TOKEN。ANTHROPIC_API_KEY 不需要设置,保持未设即可。完整清单见 Claude Code 403 排查页。
在哪里能 看到 具体是 哪个 请求、为什么 失败?
控制台 → 日志展示请求状态、模型、Token、结算费用和延迟,可按模型、API Key、状态与日期过滤。客户界面不展示供应商或故障转移链。响应中有 request_id 时请保留,即使列表暂未出现记录,也可交由支持核对最终结果与中间尝试。
我遇到 的是 unsupported_country_region_territory 或 insufficient_quota——这是 Router One 的报错吗?
不是——这两个都来自官方 OpenAI API,不是网关。unsupported_country_region_territory 是平台边缘的地区拦截;insufficient_quota 是伪装成 429 的账单额度耗尽。两者各有专页:地区限制报错修复页和 insufficient_quota 修复页。