Claude Codeをprint mode(-p)でCIやcronに組み込むけど、無限ループで課金が膨らむのを防ぎたい人向け
GitHub ActionsのCIやcronから Claude Codeを-p(print mode)で叩いて自動化を組むときに、暴走で課金が膨らむのを防ぐ保険として、コマンドに--max-turns 5などを付け足して叩く。指定しないとデフォルトは無制限で、Claudeが「完了」と判断するまで何ターンでも回り続けるため、夜間バッチや定期実行で予期しない金額が動くのを構造的に止めたい場面に必須。
--max-turns は、Claude Code をスクリプトの中から呼び出すときに「最大で何ターン回したら強制終了する」を決めるための保険装置です。指定しないとデフォルトは無制限。CI や cron の中で Claude が暴走したとき、何時間も回り続けて API 課金が膨らむのを構造的に防ぎます。
print mode 専用なので、対話画面で叩いても効きません。-p とセットで使うのが大前提です。
噛み砕くと
家電のブレーカーみたいなものです。Claude が「タスクを完了した」と自分で判断するまで何回でも応答とツール呼び出しを繰り返すのが標準動作で、これが思わぬ条件で延々ループすると電気代=API 課金がそのぶん跳ねます。--max-turns 5 と書いておけば、5 ターン目で「もう切れ」とブレーカーが落ちて、エラーで終わってくれる。
本人(Claude)に「自重して止まってね」と頼むのではなく、外側のスイッチで強制的に切る発想です。だから対話モードのように人間が画面で見張れる場面では出番がなく、自動化の中でだけ意味を持ちます。
大事な前提:このコマンド指定は -p とセットでないと効かない
公式ドキュメントには明確に "print mode only" と書かれています。つまり claude --max-turns 5 とだけ叩いて対話画面に入っても、上限はかかりません。必ず claude -p --max-turns 5 "質問内容" の形で -p を一緒に渡してください。
逆に言えば、人間が画面に張り付いて操作している対話セッションでは --max-turns は必要ありません。暴走しても自分で止められるので。出番は完全に自動化の中だけです。
「自動化スクリプトの保険装置」を例に、実際の手順を見る
GitHub Actions の CI で、PR(変更提案)が来るたびに Claude にコードレビューさせる仕組みを作る場面を想定します。レビューが終わらず延々ループしたら困るので、5 ターンで強制終了する保険を入れます。
ステップ1: まずは --max-turns なしで叩いてみる
普通に print mode でレビューを依頼します。
$ claude -p "Review this diff and list issues" < diff.txt
これだとデフォルトは無制限。Claude が「もう指摘し尽くした」と判断するまで動き続けます。タスクが単純ならすぐ終わりますが、ファイルが多くてエラーが連鎖したら、何ターンも回ってその分だけ課金されます。
ステップ2: --max-turns 5 を足す
$ claude -p --max-turns 5 "Review this diff and list issues" < diff.txt
$ echo "exit code: $?"
これで Claude が5ターン目に達したら、上限到達でエラー終了します。$? は直前のコマンドの exit code が入っている変数で、上限到達の場合は 0 以外の値(non-zero)が返ります。
ステップ3: GitHub Actions のワークフローで exit code を拾う
ワークフローの YAML 側で、exit code を判定して「上限到達でレビュー未完」とログに残します。
- name: Run Claude review
run: |
claude -p --max-turns 5 "Review this PR diff" < diff.txt
if [ $? -ne 0 ]; then
echo "::warning::5ターン上限に到達。レビュー未完の可能性あり"
fi
ここで初心者がやりがちな勘違いがあります。「Claude がエラーで止まったのに、ワークフローは成功扱いになるんじゃ?」という心配。公式ドキュメントは "Exits with an error when the limit is reached" と明示しているので、上限到達は確実に non-zero で返ってきます。CI 側で $? を見れば検知できます。
ステップ4: 金額側の保険 --max-budget-usd も併用する
ターン数で止めるのが --max-turns、金額で止めるのが兄弟分の --max-budget-usd です。高 effort のモデル(Opus + max effort 等)を使うと、1ターンで結構な額が動くことがあります。両方かけておくのが安全。
$ claude -p --max-turns 5 --max-budget-usd 1.00 "Review this PR diff"
これで「5ターン超えたら止める」と「合計 1 ドル超えたら止める」の両方の上限を同時に設定できます。どちらの条件を先に満たすかによって実際の停止タイミングは変わりますが、公式ドキュメントに組み合わせ時の挙動説明はないため、本番に乗せる前にローカルで挙動を確認しておくのが無難です。
ステップ5: ローカルでテストする
本番 CI に乗せる前に、ローカルで --max-turns 1 みたいな極端な値を試して、ちゃんとエラー終了するか確認します。
$ claude -p --max-turns 1 "Write a complex Python script with tests"
$ echo $?
# 0以外が返ってくればOK(上限到達で止まった証拠)
つまり --max-turns は何をしてくれるのか
- やってくれる: 指定したターン数で Claude を強制終了。上限到達時は exit code が non-zero になるので、CI スクリプトから検知できる
- やってくれない: 「いい感じのところで止める」みたいな賢い判断はしない。あくまで回数のハードリミット。途中で打ち切るので、出力は中途半端な状態になる可能性がある
- 意味が薄い場面: 人間が画面で対話しているセッション(
-pなし)。そもそも効かないので、対話モードの暴走対策には使えない
使いどころ3シナリオ(具体題材で再現)
シナリオ1: GitHub Actions で PR レビューを自動化する
PR が来るたびに Claude にコードレビューさせる仕組み。レビュー対象が大きすぎたり、エラーが連鎖したりすると Claude が延々考え込むことがあります。--max-turns 5 を挟んでおけば、5ターン超えたら強制終了。ワークフローのログに「上限到達」と残せば、人間が後で見て「このPRはレビュー未完だから手動で見直そう」と判断できます。10分以上の暴走を構造的に防げる。
シナリオ2: cron で毎朝ニュース要約を生成する
cron で毎朝7時に「昨日の AI 業界ニュースを要約して」と Claude を叩くスクリプトを動かしている場面。ニュースが膨大だったり、要約に必要な検索が空振りしたりすると、ターンが伸びます。--max-turns 3 --max-budget-usd 0.50 をセットで指定しておけば、ターン数と金額、それぞれ独立して上限がかかります。どちらの条件を先に満たすかで止まりますが、公式 docs に組み合わせ時の動作説明はないため、初回は実機で挙動を確認するのが無難です。毎朝の自動処理だから「気付いたら数千円課金されてた」を防ぎたい。
シナリオ3: バッチ処理で100ファイルを Claude に処理させる
スクリプトで100個のファイルを順番に Claude に渡して、各ファイルに対して「このログから異常パターンを抜き出して」と聞かせる場面。1ファイルあたりの処理が長引くと、全体の所要時間が読めなくなります。--max-turns 2 で各ファイル2ターンに制限して、上限到達したファイルは別ログに退避。後で人間が見直す運用にすれば、夜間バッチが朝までに必ず終わる安心感が手に入ります。
初心者が踏みやすい落とし穴
- 対話モードで叩いても効かない。
claude --max-turns 5とだけ叩いて対話画面に入っても、上限はかかりません。必ず-pとセットで指定する。公式が "print mode only" と明示している - 「turn」をメッセージ数と勘違いする。質問1回 = 1ターンではない。Claude が応答してツールを呼び出し、その結果を読んで次の応答を出すまでの往復1セットが1ターン。複雑なタスクだと1質問で5〜10ターン消化することもある
--max-turns 1のような極端な値で「初回応答すら返ってこない」状態になる。タスクの複雑さに合わせて、最初は 3〜10 程度から試すのがおすすめ。1や2は確実に止めたいテスト用- exit code を見ずに「成功扱い」と勘違いする。上限到達は確実に non-zero で返ってくるので、CI スクリプト側で
$?を判定すること。判定なしだと「上限で止まったのにワークフローはグリーン」という事故が起きる - 金額側の保険
--max-budget-usdを知らずに、ターン数だけで防ごうとする。高 effort モデルだと1ターンでも金額が膨らむことがある。ターン数と金額は別軸の保険なので、両方かけておくのが安全 - 上限到達で止まった時の出力は中途半端。Claude が「ここで完了」と判断する前に切られるので、出力をそのまま使うと不完全な可能性がある。CI なら「上限到達ログ」を別途残して、人間が後で確認する運用が無難
書き方
claude -p --max-turns <数字> "質問内容"
やってみるとこうなる
入力
claude -p --max-turns 5 "Review this PR diff and list issues"
出力例
(Claudeが5ターン以内に完了した場合)通常のレビュー結果が出力され、exit code は 0。
(5ターンに達した場合)途中までの出力が表示されたあと、エラーで終了する。ターミナルで echo $? を叩くと non-zero(0以外の値)が返るので、CIスクリプトから「上限到達」を検知できる。
このページに出てきた言葉
- print mode
- Claude Codeを対話画面ではなく1回限りの応答で動かすモード。<code>claude -p "質問"</code> の形で叩く。スクリプトやCIに組み込む基本形
- agentic turn
- Claudeが応答してツールを呼び、その結果を読んで次の応答を出すまでの往復1セット。<code>--max-turns</code> はこれを数える
- exit code
- コマンドが終わった時に返す数字。0が正常終了、non-zero(0以外)がエラー終了。ターミナルでは <code>$?</code> で取り出せる
- CI
- Continuous Integrationの略。プログラムの変更を自動でテスト・チェックする仕組み。GitHub ActionsやCircleCIが代表例
- cron
- 決まった時刻にコマンドを自動実行するUnixの仕組み。毎朝7時にバッチを動かす、みたいな定期処理に使う
- --max-budget-usd
- ターン数ではなく金額で上限を決める兄弟設定。<code>--max-turns</code> と組み合わせて使うと、ターン数と金額の両軸で上限をかけられる