夜間のバッチ処理が静止していた。翌朝ログを見ると何も残っていない。エラーもない。最後の記録は前日の夜、実行開始の行だけだった。
Claude Codeの claude -p(非対話モード)を使った無人実行は、スクリプトから呼び出せて便利な一方で、対話モードとは違う種類の落とし穴がある。本業でAI自動化基盤を組む中で実際に遭遇したケース、調査して判明したケースを5つ整理した。
この記事で分かること:
- 利用上限(429)でのサイレントクラッシュへの対処
- OAuth認証切れが原因で起動すらできない問題
- ワークフローエージェントの「虚偽成功」問題(v2.1.301で修正)
- タスクスケジューラからの呼び出しでシェル変数が展開されない問題
- ターン数の積み上げによる容量超過と途中終了
落とし穴1:利用上限(429)でクラッシュしても何も残らない
claude -p で実行中に Claude Pro/Max の利用上限(429エラー)に達した場合、プロセスは終了コード1で終了する。問題は通知が何も来ないことだ。
タスクスケジューラや cronで claude -p を呼んでいる場合、終了コード1は「エラーで終了」を示すが、それが「利用上限に達した」なのか「OAuth認証切れ」なのか「コードのバグ」なのかは、終了コードだけでは分からない。ログに何も残らないケースもある(--output-format json の出力が途中で破損する場合)。
対処法:
-
--output-format jsonで出力を受け取り、終了コードとJSONのパース結果を別スクリプトで記録する。JSONが壊れていた場合(パース失敗)も記録する - クラッシュを検知したら、Claude Code自体のPushNotificationツールとは独立した経路(ntfy.sh等のHTTPプッシュ通知)で通知を飛ばす。Claude Code自体が起動できない場合はPushNotificationツールも使えないため、外部経路が必要
-
CLAUDE_CODE_RETRY_WATCHDOG環境変数を設定すると、上限リセットまで自動で待機してリトライする。ただしリセットが遠い(数時間後)場合に延々と待ち続けることがある。v2.1.301で追加されたCLAUDE_CODE_RETRY_WATCHDOG_MAX_USAGE_LIMIT_WAIT_MSで最大待機時間に上限を設定できるようになった
落とし穴2:OAuth認証切れでプロセスが起動すらできない
「利用上限」とは別に、OAuth認証トークンの有効期限切れで claude -p 自体が起動できないケースがある。この場合の症状は似ていて、「プロセスが終了コード1で終了する」「ログに何も残らない」という見た目になることがある。
原因の違い:
- 利用上限:プロセスは起動するが、Claude APIへのリクエストで429が返る
- OAuth認証切れ:プロセスが起動できない、または起動直後に終了する。「Please run /login」に似たエラーが出る場合もあるが、出ないこともある
認証切れが原因の場合、落とし穴1の「利用上限の待機リトライ」は機能しない。プロセスが起動できない状態でリトライを待っても、毎回同じ理由で失敗する。
対処法:
- 認証状態を定期的に確認する仕組みを持つ。Claude Code自体が
claude auth statusコマンドで認証状態を返す - 認証切れの通知を別スクリプトで送る。
claude auth statusが異常を返したら即座にスマートフォンに通知する、というシンプルな構成が有効 - 認証トークンの有効期限をあらかじめ把握し、期限前に再認証を行う。OAuth認証の有効期限は実装によって異なる(Claude Pro/MaxはAnthropicアカウントのOAuthトークン。期限は長いが、有効期限のリフレッシュに失敗するケースがある)
落とし穴3:ワークフローエージェントが成功と報告して何もしていない
Claude Codeのワークフロー機能(複数のエージェントステップを順番に実行)に、エージェントがステップを実際には実行せず、それでも実行全体が「成功」と報告するバグがあった。
これはv2.1.301(2026-10-15公開)で修正されたが、それ以前のバージョンを使っている場合は影響がある可能性がある。症状は前述の通り「エラーなし、ログに成功の記録あり、でも何も変化していない」だ。
対処法:
- Claude Code自体を最新版に保つ。v2.1.301以降であればこの特定のバグは修正されている
- ただしこれは「このバグの修正」であって、「ワークフローの実行結果を確認する」という設計上の問題は残る。無人ワークフローの結果は、「成功ログがあった」ではなく「期待する変化が実際に起きたかどうか」で確認する
落とし穴4:タスクスケジューラからの呼び出しでシェル変数が展開されない
Windowsのタスクスケジューラ(または同様のジョブスケジューラ)から wsl.exe 経由で claude -p を呼び出す場合、コマンド文字列に シェル変数($id、$VAR、$(command) 等) を直接書くと、変数が空文字列になる。
具体例:
# これを「引数」フィールドに直接書いても $id は常に空になる
wsl.exe -d Ubuntu -- bash -lc "for id in 001 002 003; do node script.mjs $id; done"
PowerShellからwsl.exeを呼んでも、bashを直接呼んでも同じ結果になる。タスクスケジューラの「引数」文字列を経由した場合にのみ発生する問題で、.sh ファイルの中でのみ変数を使う形なら問題ない。
対処法:
# 値はリテラルで書き並べる
wsl.exe -d Ubuntu -- bash -lc "node script.mjs 001; node script.mjs 002; node script.mjs 003"
# またはループを .sh ファイルの中に書いてそのファイルを呼ぶ
wsl.exe -d Ubuntu -- bash -lc "/path/to/run-all.sh"
ループや動的な値が必要な場合は、必ずシェルスクリプトファイル(.sh)の中に書く。タスクスケジューラの「引数」フィールドにシェル変数展開を直接書くのは避ける。
これは claude -p 固有の問題ではなく、タスクスケジューラ経由の wsl.exe 呼び出し全般に当てはまる点に注意。
落とし穴5:ターン数が積み上がって容量超過・途中終了
claude -p はトークン消費が多い。1回のセッションで「同じファイルを複数回読む」「念のための確認を繰り返す」「必要のない検索を重ねる」という行動パターンになると、ターン数(ツール呼び出し回数)が積み上がる。
--output-format json の出力に含まれる numTurns がその指標だが、注意点がある:トップレベルの numTurns が実態を反映しないことがある。内部処理が別のカウントで行われ、トップレベルに乗らないケースがあることが確認されている。実際のトークン消費は modelUsage の内訳(outputTokens、cacheReadInputTokens)を確認する方が信頼性が高い。
容量超過が起きると、claude -p は途中で終了する。ここで問題になるのが「再実行したとき、同じ作業をやり直すかどうか」だ。何の工夫もなく再実行すると、すでに完了した作業を最初からやり直すことになり、さらに容量を消費する。
対処法:
- プロンプトに「同じファイルを二度読まない」「確認は1テーマ1回まで」という操作回数の明示的な制限を書く
- ターンごとの作業記録(何をしたか)をファイルに記録し、再実行時は記録を読み込んで「すでに完了した作業」をスキップする
- 容量消費の実測値を記録しておき、どの工程が重いかを把握して必要なものだけ実行するように分割する
まとめ
claude -p での無人実行は、対話モードと違って「おかしいな」と感じる人間がいない。そのため、問題が起きても気づくまでに時間がかかりやすい。
特に怖いのは「成功しているように見えるが何も起きていない」パターンだ。今回の5つの落とし穴のうち、1・3・5番がこれに該当する。「エラーがないこと」と「正しく実行されていること」は別の話だ。
自動化が増えるほど、「動いているかどうかの確認」そのものを仕組みとして組み込む必要がある。処理の完了ではなく、期待する変化が実際に起きたかどうかを確認する経路を別途持つことが、長く安定して動く無人実行基盤の基本だと感じている。
あなたの無人実行環境で、「ログに成功と残っていたが実は何も変化していなかった」という経験はありますか?
参考記事: