Troubleshooting · 限流

429 Too Many Requests — 等你看见它的时候,自动重试已经用完了

30 秒版
  1. 先看有没有 retry-after:有 = 限速,退避和降并发有用;没有 = 配额耗尽,退避永远无效,只能加额度或换路径。
  2. 别手动重试——临时 429 在你看见之前已经被自动退避重试过了,能冒到你眼前就说明它不是临时的
  3. 插件类客户端反复 429 而同一凭证 curl 正常时,主解是改用 OpenAI 兼容接口路径,调并发只是次解。

临时性的 429 是会被自动重试的——指数退避最多 10 次,而且响应带 retry-after 时会按它来等。所以真正能冒到你眼前的 429,多半已经挺过了这一轮。你再按一次回车,是在重跑一个刚刚被证伪了十次的假设。值得问的问题不是「我该等多久」,而是这个响应有没有 retry-after——因为就是这一个头,把两个共用同一个状态码的、完全不同的问题分开。

你看到的报错

以下任意一种
API Error: 429 {"type":"error","error":{"type":"rate_limit_error","message":"..."}}

429 Too Many Requests

API Error: Request rejected (429) · this may be a temporary capacity issue. If it persists, check the status page.

API Error: Request rejected (429) · spend limit reached (daily; resets ... UTC)

前三条是限流。第四条根本不是限流——它是同一个状态码承载了相反的含义,而且所有对前三条有用的直觉,用在第四条上只会让事情更糟。

两种 429:共用一个数字,其余毫无共同点

限流 — 你发得太快了耗尽 — 你的额度用完了
retry-after没有
错误类型rate_limit_errorrate_limit_error完全一样
能分辨的字段Messages API 上 error.details.error_code = enforced_spend_limit_reached
重试提示头网关的消费上限 429 会带 x-should-retry: false
退避有用吗?有用 — 它就是为这个设计的永远没用。连自动重试也一样失败,直到访问恢复
会自己好吗?会,几秒到几分钟只在它写明的重置时间,或有人把上限调高
真正能解决的动作降并发、多用缓存、把突发摊平提高档位或上限,或者换一条路径

还有两种形态值得知道,因为它们从另一个方向打破「429 就是速率限制」的条件反射:

  • 你自己给组织或工作区设的消费上限,回的是 HTTP 400 invalid_request_error,不是 429,信息以 You have reached your specified API usage limits 开头。很多人在搜一个自己压根没收到过的 429。
  • 专门针对编码代理工作区的限额是单独判定的,它可能回一个 retry-after 的 429——所以 retry-after 告诉你的是「退避值得一试」,不是「你的配额还健康」。

读头,别读信息文案

信息文案是写给人看的,各家不一样。头才是真正的契约。

终端
# <模型 id>:从 https://api.9coding.com/v1/models 里复制一个
curl -sS -D - -o /dev/null https://api.9coding.com/v1/messages \
  -H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \
  -H "content-type: application/json" \
  -H "anthropic-version: 2023-06-01" \
  -d '{"model":"<模型 id>","max_tokens":16,"messages":[{"role":"user","content":"hi"}]}' \
  | grep -iE 'http/|retry-after|x-should-retry|anthropic-ratelimit'

你要找的是:

响应头它告诉你什么
retry-after要等多少秒。它出不出现本身就是诊断结论;提前重试只会继续失败
anthropic-ratelimit-requests-remaining还剩多少请求余量
anthropic-ratelimit-input-tokens-remaining输入 token 余量(按千取整)
anthropic-ratelimit-output-tokens-remaining输出 token 余量
anthropic-ratelimit-*-reset该桶完全补满的时间,RFC 3339 格式

三种能省下真实时间的读法:

  • retry-after,某个 *-remaining 归零或接近零 → 普通限流。归零的那个告诉你撞的是哪条线,而这决定了哪种修法有用。输出 token 用完,不是靠少发请求能解决的。
  • 没有 retry-after → 现在就停止重试。等待改变不了任何事情。跳到第 5 步
  • 一个 anthropic-ratelimit-* 头都没有 → 你的端点没有把它们透传出来。你还能用 retry-after,但你失去了余量的观测能力。这件事,最好在事故之前就知道,而不是在事故之中。

有一个反直觉的性质:限额是持续补充的,不是按钟点重置的。一条按分钟表述的限制,可能在短得多的窗口上执行——每分钟 60 个请求,实际行为可能接近每秒 1 个。一次突发可以撞上一条你平均值上离得很远的线。如果用量曲线看着没问题却还在被限流,去看流量的形状,不要看总量。

按这个顺序排查

1 · 先判断你手上是哪一种 429#

跑上面那条查头的命令。下面所有分支都建立在这个判断上,而第 2 到 4 步用在「额度耗尽」上是纯粹的浪费。

2 · 别在内建重试之上再叠一层自己的重试#

如果你用 shell 循环包住了客户端,或者你正按着回车不放,你是在一个已经跑完的退避计划上再加延迟。更糟的是,你在往一个需要安静下来才能补满的桶里继续灌请求——重试本身变成了它一直空着的原因。

在做任何反应之前先分清两种显示状态:正在重试是一个带尝试次数的倒计时,它正在干活,别动它。没有倒计时直接打印出来的报错,才是重试预算用完之后的终态。

具体呈现方式请按你自己正在跑的那个客户端版本实测确认。

3 · 先降并发,再谈调高任何东西#

限流通常是被并行度塑造的,不是被总工作量塑造的。

终端
export CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY=2

同样值得做的:少跑几个并行子代理;不要几个会话同时打同一份凭证;如果是在把一个新工作负载往上加量,慢慢加——流量的陡然阶跃会触发加速度限制,哪怕你的稳态用量离档位上限还很远。

4 · 不抬高限额,也能抬高实际吞吐#

这是全页最便宜的修法,也是几乎没人想到的那个。在多数模型上,从缓存读出来的 token 不计入每分钟输入 token 限额——只有未缓存的输入、以及正在写入缓存的部分才算。具体说:cache_read_input_tokens 在限流器眼里是免费的,而 input_tokenscache_creation_input_tokens 不是。

这个差别很大。在每分钟 200 万输入 token 的限额下,如果缓存命中率是 80%,同样一条限额可以推过去大约每分钟 1000 万总输入 token。所以,把每轮都要重发的东西缓存起来——系统指令、工具定义、大段上下文文档、对话历史。一个中午被限流的工作负载,下午在同样的量下可能就很宽裕,而限额一个字都没改。

先看自己的数:如果一个长会话里 cache_read_input_tokens 接近零,这就是你手上最大的那个杠杆。

5 · 如果是额度耗尽,就别再排错了,去改点什么#

先确认客户端实际在用哪份凭证——最常见的意外是:它用的不是你刚充值的那份。

Claude Code
/status

然后:去服务商控制台看当前生效的限额和已用量,把档位或上限调上去。如果访问是被暂停到一个写明的重置时间,那个时间就是答案,你这边任何配置都挪不动它。

6 · 无人值守的场景要的是另一个设置,不是另一种心态#

CI 任务和长时间自主运行的会话里,跑到一半失败的代价比等待大得多,这时可以让客户端在容量类错误上一直重试下去,而不是用完默认预算就放弃。这个行为、它的默认尝试次数、它的上限,都在版本之间变过——请按你自己正在跑的那个客户端版本去查环境变量参考,不要从论坛帖子里抄一个数值。有一条性质一直没变,而且是最重要的那条:它在消费上限类的 429 上仍然立刻失败,因为对那种错误无限重试,只是一种更慢的失败方式。

一种和你的用量无关的 429

这里要单独点名一个模式,因为它会把人往错误的方向带上好几天。

插件类的客户端——浏览器翻译插件是最清楚的例子——发出去的默认 Prompt,对它的每一个用户都是逐字节相同的。成千上万个安装,产生出一股看起来像单一来源的请求流:同样的系统提示词、同样的形状、同样的节奏,在页面加载时成簇地涌过去。在上游看来,这样的流量很容易被判定成「同一个行为体在绕开按账号计的额度」,于是回你一个 429——而它跟你的用量毫无关系。

破绽在于它不像限流。你自己的量小得可怜。降并发毫无变化。余量头(如果你拿得到)显示还剩很多。而且它倾向于全有或全无——要么这个客户端能用,要么就是不能用——而不是随负载逐渐劣化。

真正管用的是改请求路径,不是改请求速率。把这个客户端改走 OpenAI 兼容接口、而不是原生接口,流量就有了不同的形状和不同的判定结果,通常立刻就通。降并发是次一级的措施;单独用它一般什么都不会发生,而这恰恰是很多人最后得出「我的配额坏了」这个结论的原因。

请把这一段当作实测观察,不是成文规则——没有哪家服务商会公开自己的分类器,上面的机制是从行为反推的。但验证成本很低:如果插件在拿 429、而同一份凭证用 curl 打过去是通的,那你就没有额度耗尽,再怎么退避也不会有用。

什么时候这不是你的问题

以下情况说明问题在上游,不在你的用量:

  • 你的 anthropic-ratelimit-*-remaining 头显示余量还很足,却仍然在收 429,
  • 指向同一个端点的其他客户端也在同一时间段开始失败,
  • 它是在你的工作负载没有任何变化的情况下开始的,
  • 而且你什么都没调,它自己就恢复了。

这四条凑齐,指向的是共享的上游池子或一次总体容量事件。在你为一个本来就帮不上忙的更高档位花钱之前,先去看状态页

提问题时请带上 request id——9Coding 的报错响应里都有一个,形如 (request id: 2026...)。这个 id 加上时间戳、模型名和完整报错文本,就能把那一次调用查出来。只说「用不了」的反馈没法排查。

429 和 529 是两种不同的故障

529 表示服务过载——那是他们那边的容量问题,影响所有人,跟你的额度无关。两者都会被自动重试,也都只在预算耗尽后才浮到你眼前。区别在于之后该做什么:429 在你这边有杠杆(并发、缓存、档位)。529 没有——你只能等,或者切走。用降并发去修一个 529,无害,但也无用。请看 529 过载

相关报错