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 過載

相關報錯