Claude Media
CLAUDE_CODE_RESUME_PROMPTとMAX_AGE_MSで自動再開を調整する

CLAUDE_CODE_RESUME_PROMPTとMAX_AGE_MSで自動再開を調整する

中断ターンの自動再開メッセージと、再開を諦めるまでの経過時間をCLAUDE_CODE_RESUME_PROMPTとMAX_AGE_MSで調整する方法をまとめます。

CLAUDE_CODE_RESUME_INTERRUPTED_TURNの自動再開とは

CLAUDE_CODE_RESUME_INTERRUPTED_TURNは、前回のセッションがターンの途中で終わったときに自動で再開させる環境変数です。SDKモードでの利用を想定しており、1を設定するとClaude Codeが続きから応答を返し、呼び出し側のアプリケーションがプロンプトを再送信する必要がなくなります。

この変数を設定しない場合、挙動は逆です。claude -pをSIGTERMで止めると、進行中のターンは未完了のまま残り、終了コードは143になります。再開しても中断したターンには手を付けず、次に渡したプロンプトから会話が進みます。SIGINTやAgent SDKのinterrupt()で止めたときは、ターンそのものが終わった扱いになります。

自動再開には追加の環境変数が2つあります。続きを促す文言を決めるのがCLAUDE_CODE_RESUME_PROMPT、どこまで古いセッションなら再開してよいかを決めるのがCLAUDE_CODE_RESUME_INTERRUPTED_TURN_MAX_AGE_MSです。

止め方は、変数を未設定にするか0にするかのどちらかです。Claude Codeのv2.1.221より前は、0のような偽値が効かず、CLAUDE_CODE_RESUME_INTERRUPTED_TURN=0でも再開が発動していました。古いバージョンで無効化するなら、変数そのものをunsetします。

再開が思ったとおりに動かないときの切り分け

症状から原因を探すときは、次の3つのどれに当たるかで見ます。詳しい挙動は後の節で説明します。

切り分け

再開しない・再開しすぎるときの3つの原因

  • 期限切れで再開が見送られた

    最後のメッセージがMAX_AGE_MSの上限より古いと、セッションはアイドルで立ち上がり、継続メッセージは送られません。

  • 止めたはずなのに再開した

    v2.1.221より前なら0が無視されています。それ以降でも、設定ファイルのenvが別の値で上書きしていないかを見ます。

  • 保存されたセッションがない

    --no-session-persistenceを付けた実行は、セッションをディスクに残さず、再開もできません。再開以前の問題です。

3つ目はヘルプの文面から読み取れます。v2.1.289のclaude --helpでは、このフラグは次のように説明されています。

claude --version
# 2.1.289 (Claude Code)
claude --help
#   --no-session-persistence   Disable session persistence - sessions
#                              will not be saved to disk and cannot be
#                              resumed (only works with --print)
#   -r, --resume [value]       Resume a conversation by session ID, or
#                              open interactive picker with optional
#                              search term

CLAUDE_CODE_RESUME_PROMPTで継続メッセージを差し替える

CLAUDE_CODE_RESUME_PROMPTは、CLAUDE_CODE_RESUME_INTERRUPTED_TURNが中断ターンを継続するとき、あるいは次の節で扱うdeferされたツール呼び出しを-pで再開するときに、Claude Codeが送る継続メッセージを上書きします。

既定値はContinue from where you left off.です。空文字列を設定すると既定値に戻ります。

export CLAUDE_CODE_RESUME_INTERRUPTED_TURN=1
export CLAUDE_CODE_RESUME_PROMPT="続きから作業を再開してください。"
claude -p --resume <session-id>

SDKアプリが独自のUIを持つなら、画面に出す文言とClaudeへ送る継続文言を揃えたい場面で使えます。

deferされたツール呼び出しを-pで再開するときの継続メッセージ

CLAUDE_CODE_RESUME_PROMPTにはもう一つの出番があります。hookがpermissionDecision: "defer"を返したセッションを-pで再開するときです。

典型例はAskUserQuestionです。Claudeがユーザーに質問したくても、-pで動くプロセスには答えを打ち込む端末がありません。deferは、呼び出し側のプロセスがツール呼び出しの時点でClaudeを一時停止させ、自前のUIで入力を集めてから再開する仕組みです。なお-pの実行でAskUserQuestionが使えるのは、--permission-prompt-toolで渡すMCPツールのような許可の窓口(permission host)があるときだけです。

流れ

deferの往復と、継続メッセージが送られる場所

  1. 1

    Claudeがツールを呼ぶ

    AskUserQuestionのようなツールが呼ばれ、PreToolUseフックが発火します。

  2. 2

    フックがdeferを返す

    ツールは実行されず、プロセスはstop_reason: "tool_deferred"で終了します。保留中の呼び出しはtranscriptに残ります。

  3. 3

    呼び出し側が回答を集める

    SDKの結果からdeferred_tool_useを読み、自前のUIで質問を出して回答を待ちます。

  4. 4

    同じ窓口を付けて再開する

    claude -p --resume <session-id>を実行すると、このときCLAUDE_CODE_RESUME_PROMPTの継続メッセージが送られ、同じツール呼び出しに対してPreToolUseが再び発火します。

  5. 5

    allowで続きが進む

    フックがupdatedInputに回答を入れて"allow"を返すと、ツールが実行されます。

"defer"は非対話モード(-p)でのみ有効です。対話セッションでは警告を出して無視されます。Claudeが1ターンで複数のツールを同時に呼んだ場合も無視され、通常の権限フローに進みます。resumeで再実行できる呼び出しは1件だけで、複数のうち1件だけを保留すると残りが取り残されるためです。

deferされたセッションにはタイムアウトも再試行回数の制限もありません。cleanupPeriodDays(既定30日)の保持期間を過ぎるまでディスクに残ります。回答が揃っていなければ、フックはもう一度"defer"を返せます。ループを抜けるタイミングは呼び出し側が決めます。

保留していたツールが再開時に使えないこともあります。ツールを提供していたMCPサーバーが再開したセッションに接続されていないと、フックの発火前にプロセスがstop_reason: "tool_deferred_unavailable"かつis_error: trueで終了します。deferred_tool_useは結果に含まれるので、どのツールが失われたかは特定できます。

planモードのまま再開したいときは、--resumeと一緒に--permission-prompt-toolを渡します(v2.1.246以降)。-pで再開すると、保存されていた他の権限モードは復元されません。新しいclaude -pと同じモードで始まるため、--permission-modeや--dangerously-skip-permissionsは再度指定します。ただしplanモードへ戻す再開では、これらの起動フラグを渡さないでください。渡すと再開後の実行はplanモードに戻りません。

MAX_AGE_MSで再開を諦めるまでの経過時間を決める

CLAUDE_CODE_RESUME_INTERRUPTED_TURN_MAX_AGE_MSは、ターンの途中で終わったセッションについて、transcriptの最後のメッセージがどれだけ古ければ自動再開を諦めるかをミリ秒で指定します。

上限を超えると、CLAUDE_CODE_RESUME_INTERRUPTED_TURNによる自動再開とCLAUDE_CODE_RESUME_PROMPTの継続メッセージがスキップされます。セッションはアイドルで立ち上がり、明示的な指示を待ちます。この変数はv2.1.211以降で使えます。

設定値挙動
未設定または0挙動上限なし。ただし直前のリクエストがAPIエラーで失敗したターンは、そのエラーから6時間未満のときだけ再開する
正の数値挙動APIエラー由来のターンも含め、すべてのターンにこのミリ秒数の上限を適用する
負の値または数値でない値挙動1時間の上限を適用する

Claude Code自身もこの変数を使います。agent viewでクラッシュしたセッションを再起動するとき、対話セッションから会話を引き継いだものに限り、1時間の上限を自分で設定します。長時間動かすエージェントのスポーンスクリプトでも明示しておくと、古いtranscriptに対する再起動が期限切れのプロンプトを再実行する事態を防げます。

export CLAUDE_CODE_RESUME_INTERRUPTED_TURN_MAX_AGE_MS=3600000

上の例は1時間(3,600,000ミリ秒)の上限です。数値の環境変数は3_600_000のような桁区切りや3.6e6のような指数表記も受け付けます。

修正履歴で見る、この機能の動きの変わり方

自動再開まわりの挙動はバージョンで変わってきました。手元のclaudeが古いと、この記事の説明と食い違うことがあります。

バージョン

自動再開まわりの変更(changelogより)

  1. v2.1.211MAX_AGE_MSと桁区切り表記

    整数の環境変数が1e6や64_000の表記を受け付けるようになりました。MAX_AGE_MSの対象バージョンもここからです。

  2. v2.1.2210で自動再開を止められる

    偽値が無視されて自動再開が止まらない問題が直りました。

  3. v2.1.2696時間以上前にAPIエラーで止まったターンを再実行しない

    APIエラーで失敗してから6時間以上たったターンや、MAX_AGE_MSを超えたターンを再実行しないようになりました。

  4. v2.1.274未完のバックグラウンドタスクを報告

    CLAUDE_CODE_RESUME_INTERRUPTED_TURNを付けたローカルのclaude -p --resumeで、前のプロセスが残した未完のバックグラウンドタスクが報告されるようになりました。

  5. v2.1.285--max-turnsで終わったターンを再実行しない

    --max-turnsで終わったターンが、自動再開で繰り返される問題が直りました。ターン数の上限そのものはCLAUDE_CODE_MAX_TURNSの記事で扱っています。

SDKやスポーンスクリプトでの使いどころ

Agent SDKを使うアプリがClaude Codeをサブプロセスとして起動し、ネットワーク断やプロセス再起動でターンが中断されたとします。CLAUDE_CODE_RESUME_INTERRUPTED_TURNを立てておけば、呼び出し側は同じプロンプトを再送する処理を持たずに済みます。

一方で、長く放置されたセッションを無条件に再開すると、数日前の古い指示が今のコンテキストに対して実行されかねません。MAX_AGE_MSはその安全弁です。未設定のままだと、6時間の上限が効くのはAPIエラー由来のターンだけです。定常的に動かす基盤では、数値を明示する選択肢があります。予算到達やアイドル状態からの再開を含む運用の型は、Managed Agentsの予算到達・アイドル再開の記事にあります。

対話セッションの/resumeとは別物です。利用上限のリセット後にセッションを自動で再開したい場合は、Claude Codeの利用上限リセット後の自動再開設定が扱う仕組みになり、CLAUDE_CODE_RESUME_INTERRUPTED_TURNとは制御対象が異なります。

たとえば監視プロセスが一定間隔でヘルスチェックし、応答がなければclaude -p --resumeで再起動する構成を考えます。ネットワーク障害でプロセスが長時間放置されると、復旧後の再起動で数時間前の指示が実行されます。MAX_AGE_MSをヘルスチェックの許容遅延に合わせて数分〜数十分のミリ秒数にしておけば、古すぎるセッションはアイドルで起動し、判断は監視側に戻ります。

settings.jsonでの固定と反映タイミング

シェルのexportは、そのターミナルで起動するclaudeにだけ効きます。チーム全員に同じ挙動を持たせるなら、.claude/settings.jsonのenvキーに書きます。

{
  "env": {
    "CLAUDE_CODE_RESUME_INTERRUPTED_TURN": "1",
    "CLAUDE_CODE_RESUME_INTERRUPTED_TURN_MAX_AGE_MS": "1800000"
  }
}

同じ変数をシェルと設定ファイルのenvの両方に書くと、ほとんどのセッションでは設定ファイルの値が使われます。設定ファイルの値は、シェルから引き継いだ値を置き換える形で反映されるためです。シェルの値が残るセッションの種類は、公式の設定リファレンスの「How env values interact with your shell」にまとまっています。

実行中のセッションでは、ファイルを保存した時点で追加・変更した値が反映されます。ただしOpenTelemetryのように起動時に一度だけ変数を読む機能は、再起動まで古い値のままです。ファイルから変数を消しても実行中のセッションでは未設定になりません。消した結果が効くのは、次にclaudeを起動したときです。

設定ファイルでは、変数を未設定にすることはできません。シェルのプロファイルが設定するCLAUDE_CODE_USE_VERTEXのような変数を打ち消すときは、envで空文字列("")を指定します。これはプロバイダー選択では未設定として扱われますが、サブプロセスには空の値が引き継がれます。変数ごとの扱いは異なるので、自動再開の変数を止めたいときは0を使います。

複数の設定ファイルが同じ変数を持つ場合は、管理者が配布するmanaged settingsがユーザー設定・プロジェクト設定より優先されます。CLAUDE_CONFIG_DIRのように、プロジェクト設定やローカル設定からは変更できない変数もあります。

VS Codeで復元されたタブが中断ターンを再実行するとき

VS Codeのチャットパネルには、ウィンドウの再読み込みで中断されたステップを続ける機能があり、Claude Code: Continue After Reload設定で切れます。CLAUDE_CODE_RESUME_INTERRUPTED_TURNの公式の説明も、VS Codeのチャットパネルについては別ページの「Continue conversations after a reload」を見るよう案内しています。

v2.1.285より前は、VS CodeをCLAUDE_CODE_RESUME_INTERRUPTED_TURNを設定した状態で起動すると、Continue After Reloadをオフにしていても復元されたタブが中断ターンを再実行しました。オフにしたのに続きが走るときは、バージョンを確かめます。

まとめ

CLAUDE_CODE_RESUME_INTERRUPTED_TURNとMAX_AGE_MSを組にして設定すると、放置されたセッションが数日後に勝手に動く事故を防げます。CLAUDE_CODE_RESUME_PROMPTは、継続メッセージの文言を変えたいときだけ足せば足ります。

この記事を共有:XはてブLinkedIn