claude -p 被 kill 之後 --resume,沒做完的那一輪會續跑嗎:RESUME_INTERRUPTED_TURN 與它的兩個配角
headless 任務跑到一半被 kill 掉,或者機器剛好重開。你拿 session id 用 claude -p --resume 接回去。照文件的說法,它不會把剛才沒做完的事做完,就停在那裡等你的下一句。
這時候該懷疑 resume 壞了嗎?
照官方文件的寫法,沒壞,預設就是這樣。要它接著做,得自己再開一個開關。而那個開關旁邊還掛著兩個配角,一個管「多舊的中斷不要再理」,一個管「續跑的時候對模型說哪一句話」。這篇全部來自官方文件與 changelog,版本與日期都是 2026 年 10 月 23 日取得。我沒有實際讓 claude -p 被 kill 再 resume 過,下面每一條行為都是文件的轉述,文件沒寫的地方我會在它出現的位置直接說沒寫。
kill 的那一刻,到底停在哪
要知道「中斷的回合」是什麼,得從 claude -p 收到 SIGTERM 時做了哪些事看起來。文件的說法是:用 kill 或 process supervisor 送 SIGTERM,Claude Code 以 exit code 143 結束,把進行中的那一輪留在未完成的狀態,而且不替它記錄任何 result。
收尾時它會終結還在跑的 Bash 指令的整個 process tree,跑 SessionEnd hooks 然後離開,過程中不啟動新的工具呼叫、不送新的模型請求。
停下來的那一刻有兩種姿勢。正在跑某個指令,session 裡就把它記成被殺掉。卡在權限詢問,那個詢問就原封不動地掛著、沒人回答。
想讓這一輪正常結束,文件給的做法是在停掉行程之前先送 SIGINT,或呼叫 Agent SDK 的 interrupt()。也就是說,「被 kill」跟「被叫停」是兩種不同的結尾,照文件的說法,後者那一輪有收尾,前者沒有。
2.1.236(2026 年 8 月 19 日)的 changelog 另寫了一條:print/SDK 模式下的 SIGTERM 不再在離開前記錄 interrupted turn 或合成的工具拒絕。我的讀法是現在什麼都不補,就是斷在那裡,這是推論,文件沒有把前後差別畫出來。
預設:醒來之後,先等你開口
再看 resume 那一步,文件的原句我直接貼:
When you resume the session, Claude Code leaves the interrupted turn as it is, and your next prompt drives the conversation.
中斷的那一輪原樣放著,下一步由你的下一句話決定。
我用一個自己想的類比來記它:你叫醒一個睡到一半的同事,預設他睜開眼睛先問你「接下來要幹嘛」,而不是自己掀開桌上那份做到一半的東西繼續做。理由很容易想到。他不確定自己睡著之前,到底做完了哪幾步。
這個擔心在 sessions 文件裡有對應的白紙黑字,講的是被切斷的工具呼叫:
a tool that was still running when the previous process ended, for example in a crash, doesn’t finish or get rerun when you resume. Claude sees the call marked as cut off before its result was recorded and is told to check whether it took effect before running it again, unless CLAUDE_CODE_RESUME_INTERRUPTED_TURN is set.
被切斷的工具呼叫不會自己跑完,也不會被重跑。Claude 看到的是一筆「結果還沒記下來就被切掉」的呼叫,而且被告知,再執行之前先確認它有沒有生效。
最後那個 unless 得多看一眼。句子到這裡就結束了,設了變數之後這筆被切掉的呼叫改成怎麼處理,文件沒有接下去寫。可能是照樣先檢查,也可能是直接重跑,我讀到的原文兩個方向都撐不住,所以我不猜。這一點之後會跟我的立場有關。
為什麼預設不續,文件沒給理由,只給了行為。我的推測是在避開副作用被做兩次,但那是推測。
打開之後,它送出去的是一句話
開關是 CLAUDE_CODE_RESUME_INTERRUPTED_TURN=1。變數頁的說明是:設成 1,上一個 session 在回合中途結束時就自動接續,用在 SDK 模式,讓模型繼續做而不用 SDK 重送 prompt。關掉的方式是 unset,或設成 0。
這裡有一個細節。不重送 prompt,那送什麼?送的是一句續跑訊息,預設是 Continue from where you left off.,這是 CLAUDE_CODE_RESUME_PROMPT 的預設值。你可以換成自己的句子,空字串等於用預設。同一個變數也管「用 -p 繼續一個被擱置的工具呼叫」時送的那句話。
用一小段示意把三者擺在一起,注意這是我依文件的變數名稱與語意組出來的,沒有實際跑過:
1 | # 示意:依文件的變數名稱與語意組成,沒有實際跑過 |
至於「0 到底有沒有用」,這是一段有歷史的事。2.1.221(2026 年 8 月 4 日)的 changelog 寫著,CLAUDE_CODE_RESUME_INTERRUPTED_TURN=0 原本沒有真的關掉自動接續,修正後 falsy 的值會被尊重。如果你的環境版本比這個舊,要關就直接 unset,別指望 0。這一句是我從 changelog 推出來的提醒,不是文件的建議。
接著是三件我沒查到的事。這個變數從哪一版開始存在:變數頁只在 MAX_AGE_MS 那一條標了需要 v2.1.211 以上,主變數本身沒標,changelog 裡我找得到最早提到它的是上面那條 2.1.221,但那是修復紀錄,不等於首次出現。寫進 settings.json 的 env 區塊會不會生效:變數頁對別的變數有標「Claude Code 會忽略 settings 檔 env 區塊裡的這個變數」這類註記,這個變數的描述裡沒有,但沒有註記不等於能用,我沒驗證。Agent SDK 怎麼把它傳進去:我查到的是 Agent SDK 的 sessions 文件頁裡,這個變數一次都沒出現。
2.1.267(9 月 9 日)還修過一個相關的毛病:在 -p --resume 下跑過 /compact 之類的指令再 resume,會多插進一輪莫名其妙的「Continue from where you left off.」。
真正難的那一步:這一輪到底結束了沒
上面都在講「怎麼續」。changelog 讓我意外的是,被反覆修的多半是「不該續的也被續了」。
我在 2026 年 10 月 23 日抓下來的 changelog 裡,點名這個變數的修正,扣掉只關 VS Code 面板的那一條,有七條,逐條擺出來:
- 2.1.221(8 月 4 日):設 0 沒有關掉自動接續,上面講過。
- 2.1.269(9 月 11 日):重跑了一個超過 6 小時前就以 API 錯誤收場的回合,或者比 MAX_AGE_MS 還舊的回合。
- 2.1.274(9 月 17 日):本地
claude -p --resume沒有回報上一個行程留下的未完成背景任務。 - 2.1.285(9 月 29 日):重跑了一個在
--max-turns就結束的回合。 - 2.1.296(10 月 9 日):重啟後,重跑了一個已經結束的回合,因為那一輪是被某個 MCP 工具的結果收尾的。
- 2.1.299(10 月 14 日):雲端 session 與這個變數,在工具結束了回合、同一步又載入了 skill 的情況下,重啟後有時又答了一次已經結束的回合。
- 同一版:Agent SDK 的
verbatimPrompts選項送出的、以/開頭的 prompt,被這個變數重跑時當成 slash command 執行。
七條裡有四條(2.1.269、285、296 與 299 的第一條)樣子很像:一個已經結束的回合,因為結束的方式不典型(API 錯誤、--max-turns、MCP 結果、工具加 skill 的組合),被判成「還沒結束」,於是重新跑一次。這是我對這份清單的歸納,官方沒有這樣寫,更沒有寫「所以預設才關著」。
可是把它跟前面那段放在一起看,我覺得兩件事互相印證。要判斷「這一輪是真的中途被切斷,還是正常走完了只是收尾方式比較少見」,本身就是個會一直出邊界案例的問題。被叫醒的同事要接著做之前,連「剛剛那件事到底做完沒」都得靠翻筆記去猜。
所以我的立場
預設關著是對的,我不會把它當成無人值守長任務的標準配備。
理由有兩層。第一,這個變數一旦打開,賭的是系統對「回合有沒有結束」的判斷是對的,而 changelog 在 2026 年 9 月到 10 月之間,就有四個版本在修這個判斷的漏洞。第二,被切斷的工具呼叫在設了變數之後怎麼處理,文件沒寫。如果你的任務最後一步有重複執行就出事的副作用,例如發通知、部署、扣款,那正好是文件沒交代的那塊,我不會把它押在上面。
我會考慮打開的情況,是這個任務重做一次的代價很小:整輪都在讀東西、分析、產出可以覆蓋的報告,那麼寧可重做也不要漏做。而且一開就搭 MAX_AGE_MS,後面會講為什麼。
會讓我改主意的條件有兩個。一是 changelog 上這一類「已結束卻被重跑」的修正連續好幾版沒再出現。二是文件把設了變數之後被切斷的工具呼叫怎麼處理寫出來。兩個都到了,我會把打開的門檻降低。第二個現在還沒有;第一個,最近一條點名這個變數的修正在 2.1.299(10 月 14 日),到我寫這篇時的 2.1.309(10 月 22 日)這十版沒有再點名,十版算不算「好幾版」,我還不想現在就下結論。
兩道年限,數字別看岔
MAX_AGE_MS 管的是「中斷的那一則轉錄訊息,離現在多久以內才值得續」。變數頁的規則有四條,我把容易讀岔的地方標出來:
Spawn scripts for long-running agents can set this so a restart against an old transcript doesn’t re-run a stale prompt.
不設或設 0,是沒有上限,這不是「立刻過期」,方向剛好反過來。例外是某些回合,例如以 API 錯誤收尾的,只在最後一則訊息的 6 小時內才會續,這是不設變數時的預設。設成正值,所有回合都受這個上限約束,包含剛剛那種 6 小時的回合。設成負值或非數字,會套用 1 小時的上限。最後這一條有個陷阱:你不小心寫成 1h 或 "60m",它不會報錯,只會悄悄變成 1 小時。這一點是我從那段文字推出來的,沒有實際試過。
毫秒要自己換算:1 小時是 3600000,6 小時是 21600000。
「6 小時」那條,原文用的是 such as,意思是以 API 錯誤收尾只是例子之一,其他哪些回合也屬於這一類,文件沒列。所以我沒辦法替你列出完整名單。
VS Code 面板是另一套,而且會把這組變數拿掉
如果你在 VS Code 裡用,這整篇都不適用。文件寫得很明白:
the extension removes those variables before it starts the panel’s sessions.
放在 VS Code 的環境或 environmentVariables 設定裡的任何 CLAUDE_CODE_RESUME_ 開頭變數,在面板裡都沒有作用。面板走自己的機制:2.1.274(9 月 17 日)加入 Continue After Reload,重新載入視窗打斷了某一步,對話回來時會接著做,並在聊天裡標一則通知;超過一小時,或 session 在別處開著,就回到閒置。想關,到設定取消勾選。這個 1 小時跟 CLI 與 SDK 那邊的 6 小時加 MAX_AGE_MS 出自不同頁面、不同機制,不能互相借用。
名稱相近的 background agents 與 Remote session 接續,例如 2.1.196 寫的 daemon 重啟後被殺掉的 worker 自動接回,是另一條路,跟這篇的 claude -p --resume 加環境變數無關。
回到那個被叫醒的人
回頭看,這組變數其實在回答一個問題:叫醒一個做到一半就被打斷的同事,你想讓他自己決定要不要接著做,還是你每次都親口交代。
預設是後者,成本是你得多說一句話。打開之後是前者,成本是你要信任他對「剛剛那件事做完了沒」的判斷,而這個判斷,正是上面那幾條修正在反覆補洞的地方。
你的任務如果被切在最糟的那個位置重啟,漏做你能接受,就什麼都不用設。寧可重做,才輪得到 MAX_AGE_MS 要設幾毫秒。動手之前,先想清楚你屬於哪一邊。


















