Claude Media
CLAUDE_CODE_MAX_TURNSとは — セッションのターン数を上限で止める環境変数

CLAUDE_CODE_MAX_TURNSとは — セッションのターン数を上限で止める環境変数

CLAUDE_CODE_MAX_TURNSは非対話実行のターン数に上限を設ける環境変数です。--max-turnsとの優先順位、settings.jsonのenvとの関係、金額・時間の上限との使い分けを扱います。

CLAUDE_CODE_MAX_TURNS は、Claudeが自律的に応答とツール実行を繰り返す「ターン」の回数に上限をかける環境変数です。明示的な上限が渡されなかったときの既定値として働き、CLIフラグの --max-turns と同じ効果を持ちます。上限に達するとClaude Codeはエラーで終了するので、暴走した自動実行がAPI呼び出しを重ね続ける事態を防げます。

上限をどこに置くか決めるための前提

この変数が決めるのは1つだけです。Claudeが1回のやり取りのなかで応答とツール呼び出しを重ねる回数の天井です。数値を指定しなければターン数に上限はなく、Claudeは目的を達成するまでツールを使い続けます。

ここでいう1ターンの定義は、Agent SDKのドキュメントにあります。Claudeがツール呼び出しを含む出力を返し、実行結果がClaudeに戻るまでの1往復が1ターンです。ツール呼び出しを含まない最終応答でループが終わります。1ターンのなかで複数のツールが並列に動くこともあるため、ターン数とツール呼び出しの回数は一致しません。SDKの max_turns は「ツール使用を伴うターンだけ」を数えると説明されています。

--max-turns を毎回のコマンドに付けるかわりに、この環境変数へ既定値を1つ入れておけば、フラグを省略したすべての実行に同じ上限がかかります。両方を指定した場合は --max-turns が優先されます。

上限を外したいときは、変数を削除するかフラグを付けずに実行します。数値を書き換えて上限を緩めるのではなく、変数の有無で「上限あり/なし」を切り替える設計です。

実機で見えたこと(v2.1.287)

手元のv2.1.287で確認できた範囲を書いておきます。モデルを呼ぶ操作は避け、--help と --version だけを使いました。

claude --version
# 2.1.287 (Claude Code)
 
claude --help | grep -i "max-turns"
# (出力なし)
 
claude --help | grep -A1 "max-budget-usd"
#   --max-budget-usd <amount>   Maximum dollar amount to spend on API
#                               calls (only works with --print)
 
CLAUDE_CODE_MAX_TURNS=0 claude --version
# 2.1.287 (Claude Code)

1点目は、claude --help の一覧に --max-turns が出てこないことです。CLIリファレンスには載っているフラグなので、フラグの存在を --help だけで確かめようとすると見落とします。--max-budget-usd のほうは一覧に出ます。

2点目は、CLAUDE_CODE_MAX_TURNS=0 を付けても --version はエラーにならないことです。公式の「起動時に拒否される」が効くのは、実際にセッションを始める経路だと考えられます。無効な値のエラー文言までは、モデル呼び出しを避けたため確認していません。値の検証を自動化の事前チェックに組み込む場合は、--version では代わりにならない点に注意が必要です。

設定のしかた

シェルの環境変数として渡すのが基本です。

export CLAUDE_CODE_MAX_TURNS=10
claude -p "リポジトリ内のTODOコメントを一覧化して"

1回だけ上限を変えたいときは、コマンドの前に置く形でも渡せます。

CLAUDE_CODE_MAX_TURNS=5 claude -p "テストを実行して失敗を修正して"

フラグを直接書く方法もあります。挙動は環境変数と同じで、上限に達すればエラー終了します。

claude -p --max-turns 3 "query"

チームや実行環境で固定値を共有したい場合は、settings.json の env キーに書きます。

{
  "env": {
    "CLAUDE_CODE_MAX_TURNS": "15"
  }
}

env は「すべてのセッションに環境変数を適用する、またはチームに展開する」ための設定キーとして公式に説明されています。プロジェクト直下の .claude/settings.json に書けば、そのリポジトリでClaude Codeを起動する人に同じ既定値が配られます。ただし、多くの env の値は各メンバーがフォルダを信頼した後に適用されます。複数リポジトリを扱うセッションでは、各リポジトリの env は読まれません。同じ env キーには、中断したターンの自動再開を調整するCLAUDE_CODE_RESUME_PROMPTとMAX_AGE_MSのような変数も並べて書けます。

ここで落とし穴になるのが、シェルで export した値との関係です。環境変数リファレンスによると、シェルと設定ファイルの env に同じ変数があるとき、ほとんどのセッションでは設定ファイル側の値が使われます。Claude Codeが env の各項目をプロセスの環境変数へ書き込み、シェルから引き継いだ値を置き換えるためです。例外のセッションもあるので、確実に一時的な上書きをしたいなら、環境変数ではなく --max-turns を付けます。フラグは環境変数より優先されます。

くらべる

上限の置き場所と優先関係

その実行だけ

--max-turns

コマンドに明示するので、環境変数が何であっても勝ちます。CIのジョブごとに上限を変えたいときの置き場所です。

既定値

環境変数 / settings.jsonのenv

フラグを渡さなかった実行すべてに効きます。env に書いた値はシェルの export より優先されるのが通常の挙動です。

管理設定(managed settings)は設定の優先順位で最上位にあたり、組織が env を配る経路にもなります。ただし配られるのはあくまで環境変数の値です。コマンドラインの --max-turns は環境変数より強いので、管理設定に CLAUDE_CODE_MAX_TURNS を入れても、フラグを付けた実行までは縛れません。組織として強制力を持たせたいなら、この変数だけに頼らず、ジョブ側のタイムアウトと金額の上限を併用する構成になります。

上限に達するとどうなるか

上限のターン数を使い切ると、Claude Codeはエラーで終了します。v2.1.285以降、上限で終わったターンが自動で再実行されることはありません。再開したいときは、新しいセッションを始めるか、claude --resume で前の会話を呼び戻します。Agent SDKから呼ぶ場合は、結果メッセージの subtype が error_max_turns になり、total_cost_usd や num_turns などの情報は上限到達後も返ります。

CLAUDE_CODE_RESUME_INTERRUPTED_TURN を併用している環境には、注意点があります。v2.1.285より前は、--max-turns で終わったターンをこの自動再開が再実行する不具合がありました。上限で意図的に止めたはずの作業が、再開のたびに走り直す形です。v2.1.285で修正されています。

適切な上限の値はタスクの粒度で変わります。1ファイルの単純な修正や定型の整形なら数ターンで終わるので、低めの上限でも打ち切られる心配は小さくなります。複数ファイルにまたがるリファクタリングやテスト修正では、ツール呼び出しの往復が増えます。上限を絞りすぎると、完了できたはずのタスクが途中で終わります。

手順

上限の値を決める手順

  1. 1

    余裕のある値で走らせる

    最初は大きめの数字を CLAUDE_CODE_MAX_TURNS に入れ、普段のタスクを -p で数回実行します。

  2. 2

    消費したターン数を記録する

    SDKのドキュメントは、結果メッセージが total_cost_usd・usage・num_turns・session_id を持つと説明しています。CLIのJSON出力に同じキーが出るかは、手元では未確認です。出なければ、打ち切られた回数を目安にします。

  3. 3

    実測の少し上まで絞る

    最大で何ターン使ったかが分かったら、その値に余裕を足して上限にします。途中終了が出始めたら、値を戻すか、タスクを小さく分けます。

--input-format stream-json で標準入力からメッセージを流し込む構成には、少し複雑な挙動があります。Claudeの作業中にこちらから新しいメッセージを送っても、そのメッセージはいったんキューに積まれます。現在のターンが上限で終わったあと、独立した新しいターンとして、新しい上限つきで実行されます。上限は会話単位ではなく、送られたメッセージごとに数え直されます。

数え直されるのはターン数だけです。金額の合計は同じ会話のあいだ積み上がり続けます(/clear で最初からやり直し)。--max-budget-usd に達すると、以降のメッセージはすべて金額超過の結果(error_max_budget_usd)で終わります。ストリーム入力で長く走らせる常駐型の構成では、ターン上限は「1メッセージあたりの暴走防止」、金額上限は「会話全体の天井」と役割が分かれます。

SDKの結果の表では、error_max_turns で終わった場合に最終結果の result フィールドは付きません。上限で止まった実行から成果物の文面を受け取る前提の処理は、この分岐を先に書いておく必要があります。

もう1つ、v2.1.281では、モデルが「解釈できないツール呼び出し」と「出力上限による打ち切り」を交互に繰り返すと、--max-turns を無視して再試行し続けるターンがありえる不具合が直っています。ターン上限の保護が効かないケースが過去にあった、という履歴は、暴走防止を上限1本に頼らない根拠になります。

対話セッションでの挙動は公式に書かれていない

--max-turns は公式ドキュメントで「印刷モード限定(print mode only)」と書かれています。印刷モードとは -p を付けて起動する非対話実行のことで、claude と打って開く通常のセッションとは別の実行形態です。

CLAUDE_CODE_MAX_TURNS の説明には「印刷モード限定」という言葉がありません。一方で「--max-turns と同じ効果」とされているため、対話セッションで効くかどうかは公式から読み取れません。-p を使った非対話実行での利用を前提にするのが無難です。

何を止めたいかで上限を選ぶ

自律実行の暴走を止める手段は3系統あり、それぞれ止める対象が違います。

上限

暴走を止める3つの上限

  • ターン数

    --max-turns は -p 実行(印刷モード)限定です。CLAUDE_CODE_MAX_TURNS なら既定値を環境変数で置けます。

  • 金額

    --max-budget-usd。これも印刷モード限定で、対応する環境変数はなくフラグで指定します。

  • 実行時間

    CI側のジョブタイムアウト。GitHub Actionsなら timeout-minutes です。Claude Codeの設定の外側にあります。

--max-budget-usd はサブエージェントの支出も合算して判定します。上限に達するとサブエージェントの新規起動が Budget limit reached で失敗し、実行中のバックグラウンドサブエージェントも止まります。この強制はv2.1.217以降の挙動です。--continue や --resume で会話に戻ったとき、以前の実行から復元された合計は上限に数えられません。再開のたびに予算が新しく始まる点は、日次で同じ会話を継ぎ足す運用では見落としやすい点です。

1本だけに頼ると穴が残ります。ターン数だけに頼ると、1ターンが長いツール実行を含む場合に所要時間が読めません。タイムアウトだけに頼ると、無駄なターンを重ねた末に時間切れとなり、コストだけが積み上がります。

具体的な設定はClaude CodeをGitHub Actionsに組み込むとClaude CodeをGitLab CI/CDに組み込むにあります。ジョブ側の書き方はそちらで確認できます。

よくある質問

上限に達したとき、Claudeが行った編集は残りますか

エラー終了の時点までに行われたファイル編集やコミットは、そのまま作業ツリーに残ります。会話は打ち切られるので、続きは新しいセッションか claude --resume で行います。

まとめ

CLAUDE_CODE_MAX_TURNS は -p の自動実行に既定の上限を置く変数で、--max-turns のフラグが優先されるため、管理設定で全員に強制する用途には向きません。非対話セッションの実行状態を監視ホストへ報告したい場合はCLAUDE_CODE_BG_TASKS_REPORT_RUNNINGの解説。環境変数全体の見取り図はClaude Code環境変数リファレンスも参考になります。

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