一個 API 請求在串流時一直失敗,Claude Code 把它重送了 21 次。

這是 CHANGELOG 2.1.285 修掉的 bug,原文寫的是「a failing API request being retried up to 21 times when streaming kept failing」。官方文件講的預設明明是重試 10 次,多出來的那一截,來自串流失敗之後改走的非串流備援:它沒有跟原本的請求共用額度,自己又拿到一整份新的重試次數。修法是讓備援改用同一份預算。下一版 2.1.286(也是我寫這篇時 CHANGELOG 上最新的一版)再往前一步,把計數單位改成「一整次模型呼叫」,照預設設定,一次失敗的呼叫最多送出 14 個請求。

bug 已經修掉了,讓我在意的是:重試次數這個數字,連官方自己都在最近連續兩版裡重新數過。你在自己的 CI 裡想「保險一點,把重試調大」,調的其實是一個你看不太到全貌的東西。

所以這篇要回答的問題很窄。Claude Code 打 API 失敗的時候,你手上有四顆旋鈕:CLAUDE_CODE_MAX_RETRIES、CLAUDE_CODE_RETRY_WATCHDOG、API_TIMEOUT_MS、CLAUDE_STREAM_FIRST_BYTE_TIMEOUT_MS。什麼情況該轉哪一顆?

先交代一下:這篇只讀了官方的 errors 頁、環境變數表、headless 頁 與 CHANGELOG,沒有實際跑過任何一個設定,也沒量過重試實際要等多久。下面的預設值與上限都照文件原文,至於重試在真實環境裡會耗掉多少時間,我沒有數據可以給你。

送貨員按門鈴

快遞送到你家,按門鈴沒人應。正常的送貨員會隔一陣子再按,等的間隔越拉越長,按到第十次還是沒人,就貼張招領單走人。

這就是 Claude Code 的預設行為。文件原文是「retries transient failures up to 10 times with exponential backoff before showing you an error」,伺服器錯誤、overloaded、逾時、斷線、暫時性的 429 節流,都在這個範圍。你在畫面上看到錯誤訊息的時候,它已經把該做的重試做完了。重試期間 spinner 會顯示 Retrying in Ns · attempt x/y 的倒數,人在終端機前面的話,看得到它在努力。

有一種失敗它刻意不重試:回應已經送到一半,Claude 已經完成了一段文字或一個工具呼叫,這時候才斷線或吃到伺服器錯誤。文件給的理由很直接,重跑「could execute the same tool calls twice」。送貨員已經把一半的貨搬進你家客廳,他不會再從頭搬一次,因為那會讓你收到兩份。Claude Code 會保留已完成的部分,顯示 The response above may be incomplete 這類訊息,從已完成的工具結果接著往下走。

所以有一整類失敗,重試次數調多高都沒用,它根本不在重試的路徑上。

沒人在家的那一天

現在換個情境。CI job、eval harness、跑在遠端的 worker,三更半夜沒有人盯。服務剛好在那段時間過載,回了一串 529。

直覺的做法是把 CLAUDE_CODE_MAX_RETRIES 調大,叫送貨員多按幾次。這條路官方已經替你堵掉了:2.1.186 的 CHANGELOG 寫「Changed CLAUDE_CODE_MAX_RETRIES to cap at 15; for unattended sessions, use CLAUDE_CODE_RETRY_WATCHDOG instead」。上限壓在 15,同一句話裡就把你指去另一顆旋鈕。環境變數表對 CLAUDE_CODE_MAX_RETRIES 的說明也是同一個意思,最後一句是「For unattended sessions that need to wait through longer outages, set CLAUDE_CODE_RETRY_WATCHDOG instead」。

兩顆旋鈕看起來像同一類東西,管的卻是不同的事。MAX_RETRIES 回答「按幾次門鈴」。RETRY_WATCHDOG 回答的是另一個問題:要不要乾脆在門口等到你回家。

設成 1 之後,429 和 529 這兩種容量類錯誤會無限重試,不再按 CLAUDE_CODE_MAX_RETRIES 的次數放棄。重試之間最多退避 5 分鐘;如果回應裡帶了限額重置時間,就等到重置為止,所以碰到使用量上限的 session 會把剩下的時段等完。從 v2.1.199 起,它另外把伺服器錯誤、逾時、斷線這些非容量類暫時性錯誤的預設重試次數拉到 300,文件說大約是三小時的退避,同時拿掉你自己明確設定 CLAUDE_CODE_MAX_RETRIES 時的 15 次上限。

設法照官方環境變數頁的寫法,在 shell 裡先 export 再啟動:

1
2
export CLAUDE_CODE_RETRY_WATCHDOG="1"
claude

或寫進 settings.json 的 env 區塊,這樣不管 claude 是怎麼被叫起來的都會生效:

1
2
3
4
5
{
"env": {
"CLAUDE_CODE_RETRY_WATCHDOG": "1"
}
}

兩段的格式是照抄官方範例的,官方範例裡放的是 API_TIMEOUT_MS,我把變數名和值換成 watchdog 的。

帳單寄到的時候,它不等了

watchdog 的「無限等」只對容量類錯誤成立。環境變數表在同一格裡寫著:「Claude Code fails at once when a standard-speed request gets a 429 that reports a spend limit or exhausted usage credits, even one from a gateway spend cap that resets on a schedule.」標準速度的請求只要吃到回報花費上限或額度用完的 429,就算那是 gateway 定期會重置的花費上限,它也立刻失敗。

回到送貨員。對方不在家,他可以在門口等;可是這是一件貨到付款的包裹,而你的帳戶被凍結了,他等到天亮也收不到錢,所以直接退件。

這條規則是後來才加的。文件同一段寫「Before v2.1.239, the watchdog retried these indefinitely」,2.1.239 的 CHANGELOG 也寫 watchdog 改成碰到組織花費上限與額度用完時「fails immediately」,不再無限期等重置。換句話說,舊版碰到帳單類的 429 會一直等到重置;現在連 gateway 那種會定期重置的花費上限,它都不等了。為什麼這樣改,CHANGELOG 只寫了改成什麼,沒寫理由。

注意兩種「上限」在文件裡的待遇不同。帶有限額重置時間的使用量上限,watchdog 會等到時段重開;回報花費上限或額度用完的 429,它立刻失敗。如果你的無人值守 job 最常死在帳單上,開 watchdog 救不了你,該補的是花費上限的監控與調整。另外,2.1.281 才修掉「watchdog 在一連串 429/529 等待之後,碰到第一個 5xx 或斷線就失敗」的問題,版本比這舊的話,別太相信它撐得住。

上面那句講的是標準速度的請求。fast mode 的請求,環境變數表直接把你指到另一頁 fast mode 的限額處理,那頁的說法是遇到限額會自動退回標準速度。有開 fast mode 的話,要另外讀那一頁。

所以無人值守那一格,我的判準是:失敗大多來自過載或暫時性網路問題,開 watchdog;失敗大多來自花費上限,watchdog 幫不上忙。還有一件事是我自己的判斷,文件沒寫:一個可能退避三小時的 session,外層的 CI job 最好有自己的逾時。不然「它還在耐心重試」和「它卡死了」,從外面看是同一個畫面。

腳本裡,你要的是它早點放棄

第三種情境剛好反過來。你在寫一支腳本,外層已經有自己的重跑邏輯,或者有排好的下一輪會接手。這時候讓 Claude Code 在裡面按十次門鈴、每次越等越久,只是把「出事了」這個訊號往後拖。

errors 頁對 CLAUDE_CODE_MAX_RETRIES 的註解就寫著這個用途:「Lower it to surface failures faster in scripts」。調低,讓失敗早一點浮出來,交給外層決定要不要再來一次。文件沒有建議一個具體的數字,我也不替它編一個;你外層的重跑間隔有多長,大概就決定了裡面值得等多久。

這裡有一個意外好用的東西。用 headless 模式跑的時候,Claude Code 重試之前會送出一個 system/api_retry 事件,裡面有 attempt(從 1 起算)、max_retries、retry_delay_ms、error_status(沒拿到 HTTP 回應時是 null)和 error 類別,像 rate_limit、overloaded、server_error。你的外層程式讀得到它正在第幾次、下一次要等幾毫秒、卡在哪一類錯誤,不用盯著 log 猜。

max_retries 這個欄位的說明值得多看一眼:「total retries permitted for this failure’s cause, which can be fewer than the session-wide budget」。它是這一類失敗允許的次數,可能比整個 session 的預算還少。開頭那個 21 次的 bug、2.1.286 改成以整次模型呼叫計算,再加上這一句,「重試幾次」就很難當成一個單純的全域數字來看:計數單位最近剛改過,不同原因的失敗分到的次數也可能不一樣。

說到計數,有一個數字我到現在還拼不起來:2.1.286 那個「最多 14 個請求」。預設重試 10 次,為什麼上限是 14,CHANGELOG 沒有說明。errors 頁提到串流停滯的那一次重送是算在 10 次預算之外的,但光靠這幾句能不能剛好湊出 14,我算不出來。如果你的 CI 剛好有 system/api_retry 的事件紀錄,數一數一次失敗的呼叫實際送了幾個請求,應該就是最快的答案。

慢,不等於失敗

最後一種情況最容易被誤診成重試問題。請求其實沒有失敗,只是你公司的 proxy 或 gateway 習慣把整個回應扣住,等全部完成才一次吐給你。這時候你會看到這種訊息:

1
API Error: No response from API (waited 3m, then 10m on the retry). If a proxy or gateway on your network holds responses until they complete, raise API_TIMEOUT_MS or CLAUDE_STREAM_FIRST_BYTE_TIMEOUT_MS to wait longer.

訊息自己就把解法寫出來了,而且它點名的兩顆旋鈕都跟重試次數無關。我的讀法是,這種情況卡住的是「等多久」,不是「重送幾次」,proxy 不放行,按幾次門鈴都一樣。

API_TIMEOUT_MS 是單一請求的逾時,預設 600000 毫秒,也就是 10 分鐘,上限 2147483647。超過上限有個陷阱,文件原文是「Values above the maximum overflow the underlying timer and cause requests to fail immediately」。你以為設了一個超大的值叫它等久一點,實際上是讓每個請求當場失敗。

CLAUDE_STREAM_FIRST_BYTE_TIMEOUT_MS 管的是串流請求的第一個位元組要在多久內到,需要 v2.1.242 以上,設定值會被夾在 10 秒到 30 分鐘之間。這兩個計時器跟 Claude Code 裡其他計時器(Bash、MCP 工具那些)怎麼分工,我在〈它不是壞掉,是被計時器砍掉:Claude Code 的 timeout 與輸出上限〉整理過,這裡就不重複。

另一條路是乾脆換一個模型繼續跑,那是〈Claude Code Fallback Models 完整教學〉講的 --fallback-model。重試是同一個送貨員再按一次門鈴,fallback 是換一個送貨員。

所以,轉哪一顆

先問有沒有人在看。人在終端機前面,什麼都不用設,預設的 10 次加上畫面上的倒數就夠了。沒人看,開 CLAUDE_CODE_RETRY_WATCHDOG=1,不要去碰 MAX_RETRIES 的上限。外層有自己的重跑邏輯,把 MAX_RETRIES 調低。錯誤訊息裡出現「No response from API」,問題在計時器,轉 timeout。

會讓我改掉第二條的只有一種情況:你的無人值守 job 失敗紀錄裡,大多數是花費上限或額度用完的 429。那 watchdog 一碰到就會立刻失敗,開不開差別不大,該處理的是帳單那一側。

原文來源:Error reference - Claude Code Docs、Environment variables - Claude Code Docs、Run Claude Code programmatically - Claude Code Docs、Claude Code CHANGELOG(皆為 10 月 1 日讀取的版本)