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
余裕のある値で走らせる
最初は大きめの数字を
CLAUDE_CODE_MAX_TURNSに入れ、普段のタスクを-pで数回実行します。 - 2
消費したターン数を記録する
SDKのドキュメントは、結果メッセージが
total_cost_usd・usage・num_turns・session_idを持つと説明しています。CLIのJSON出力に同じキーが出るかは、手元では未確認です。出なければ、打ち切られた回数を目安にします。 - 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環境変数リファレンスも参考になります。