429 Too Many Requests — 等你看見它的時候,自動重試已經用完了
- 先看有沒有
retry-after:有 = 限速,退避和降併發有用;沒有 = 配額耗盡,退避永遠無效,只能加額度或換路徑。 - 別手動重試——臨時 429 在你看見之前已經被自動退避重試過了,能冒到你眼前就說明它不是臨時的。
- 外掛類客戶端反覆 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_error | rate_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_tokens 和 cache_creation_input_tokens 不是。
這個差別很大。在每分鐘 200 萬輸入 token 的限額下,如果快取命中率是 80%,同樣一條限額可以推過去大約每分鐘 1000 萬總輸入 token。所以,把每輪都要重發的東西快取起來——系統指令、工具定義、大段上下文文件、對話歷史。一箇中午被限流的工作負載,下午在同樣的量下可能就很寬裕,而限額一個字都沒改。
先看自己的數:如果一個長會話裡 cache_read_input_tokens 接近零,這就是你手上最大的那個槓桿。
5 · 如果是額度耗盡,就別再排錯了,去改點什麼#
先確認客戶端實際在用哪份憑證——最常見的意外是:它用的不是你剛充值的那份。
/status
然後:去服務商控制台看當前生效的限額和已用量,把檔位或上限調上去。如果訪問是被暫停到一個寫明的重置時間,那個時間就是答案,你這邊任何配置都挪不動它。
6 · 無人值守的場景要的是另一個設定,不是另一種心態#
CI 任務和長時間自主執行的會話裡,跑到一半失敗的代價比等待大得多,這時可以讓客戶端在容量類錯誤上一直重試下去,而不是用完預設預算就放棄。這個行為、它的預設嘗試次數、它的上限,都在版本之間變過——請按你自己正在跑的那個客戶端版本去查環境變數參考,不要從論壇帖子裡抄一個數值。有一條性質一直沒變,而且是最重要的那條:它在消費上限類的 429 上仍然立刻失敗,因為對那種錯誤無限重試,只是一種更慢的失敗方式。
一種和你的用量無關的 429
這裡要單獨點名一個模式,因為它會把人往錯誤的方向帶上好幾天。
外掛類的客戶端——瀏覽器翻譯外掛是最清楚的例子——發出去的預設 Prompt,對它的每一個使用者都是逐位元組相同的。成千上萬個安裝,產生出一股看起來像單一來源的請求流:同樣的系統提示詞、同樣的形狀、同樣的節奏,在頁面載入時成簇地湧過去。在上游看來,這樣的流量很容易被判定成「同一個行為體在繞開按賬號計的額度」,於是回你一個 429——而它跟你的用量毫無關係。
破綻在於它不像限流。你自己的量小得可憐。降併發毫無變化。餘量頭(如果你拿得到)顯示還剩很多。而且它傾向於全有或全無——要麼這個客戶端能用,要麼就是不能用——而不是隨負載逐漸劣化。
真正管用的是改請求路徑,不是改請求速率。把這個客戶端改走 OpenAI 相容介面、而不是原生介面,流量就有了不同的形狀和不同的判定結果,通常立刻就通。降併發是次一級的措施;單獨用它一般什麼都不會發生,而這恰恰是很多人最後得出「我的配額壞了」這個結論的原因。
curl 打過去是通的,那你就沒有額度耗盡,再怎麼退避也不會有用。
什麼時候這不是你的問題
以下情況說明問題在上游,不在你的用量:
- 你的
anthropic-ratelimit-*-remaining頭顯示餘量還很足,卻仍然在收 429, - 指向同一個端點的其他客戶端也在同一時間段開始失敗,
- 它是在你的工作負載沒有任何變化的情況下開始的,
- 而且你什麼都沒調,它自己就恢復了。
這四條湊齊,指向的是共享的上游池子或一次總體容量事件。在你為一個本來就幫不上忙的更高檔位花錢之前,先去看狀態頁。
提問題時請帶上 request id——9Coding 的報錯響應裡都有一個,形如 (request id: 2026...)。這個 id 加上時間戳、模型名和完整報錯文字,就能把那一次呼叫查出來。只說「用不了」的反饋沒法排查。
429 和 529 是兩種不同的故障
529 表示服務過載——那是他們那邊的容量問題,影響所有人,跟你的額度無關。兩者都會被自動重試,也都只在預算耗盡後才浮到你眼前。區別在於之後該做什麼:429 在你這邊有槓桿(併發、快取、檔位)。529 沒有——你只能等,或者切走。用降併發去修一個 529,無害,但也無用。請看 529 過載。
相關報錯
- 529 過載 — 他們那邊的容量問題,不是你這邊的額度問題
- 401 Unauthorized — 兩層認證,一樣的報錯資訊
- TLS 證書錯誤 — 那個一點重試預算都沒有的故障
- 全部報錯速查 — 排查手冊總表