Mac の launchd で claude -p を定期実行するときの本当の問題は、設定の難しさではなく、止まっても気づけないことです。筆者は週次の記事生成・リサーチ・振り返りノート作成を launchd で回していて、ログイン切れ、トークンの 401、ファイル数上限、スリープ、macOS の権限制限で、それぞれ別の形で止まりました。この記事は、その症状・原因・対処と、止まりを見つける方法の記録です(2026年10月時点)。
n8n や Make と cron のどちらを選ぶかという話は n8n・Make とコード自動化の境界線に書きました。こちらは、cron や launchd を選んだあとに実際に起きたことの実践編です。
結論:壊れ方は静かで、成功する週が混ざる
定期実行の失敗は、エラーで派手に落ちるより、何も作られないまま次の週になる形で現れました。デスクトップアプリは動き続けるので、日常の作業では異変に気づけません。見張る対象は終了コードではなく、日付つきログの結果行と成果物の更新時刻です。
症状・原因・対処の対応表
| 症状 | 原因 | 対処 | 確度 |
|---|---|---|---|
| 週次ジョブがまとめて止まる。ログに「Failed to authenticate: OAuth session expired …」 | キーチェーン側のログイン切れ | ターミナルで claude を起動し /login をやり直す |
復旧を確認済み(2026-10-13) |
claude setup-token で出したトークンが 401 |
サーバー側で即 401。原因は特定できず | トークンを使わず、キーチェーン認証で動かす方式に切り替え | 切り替えで回避(2026-08-17) |
| 「low max file descriptors」と出て断続的に落ちる | launchd 既定のファイル数上限(soft 256) | 入口スクリプトで ulimit -n 10240 |
エラー文からの推定。効果は未実証 |
| 日曜夜のジョブが失敗し、制限時間が実時間で 7.5 時間かかる | Mac のスリープ中に一瞬起動。timeout がスリープ中の時間を数えない |
実行時刻とスリープ設定を見直す | 2 週続けて成果物が作られなかった |
~/Desktop や ~/Documents で「Operation not permitted」 |
macOS の権限制限(TCC) | 参照資料をホームに複製して読む。書き込みは一時置き場に逃がす | 回避を確認済み |
launchctl list は正常に見えるのに成果物が無い |
終了コードと追記専用の err.log は古い失敗が残る | 日付つきログの rc= 行と成果物の更新時刻で見る |
実際に誤診した |
ログイン切れ:週次の 3 ジョブが一度に止まった
2026年10月11日、キーチェーン側のログインが切れて、claude -p が「Failed to authenticate: OAuth session expired …」で失敗しました。止まったのは週次の記事生成・週次リサーチ・週次の振り返りノート作成の 3 本です。ログを残すだけの処理も同じ理由で止まっていました。
気づきにくかった理由は、デスクトップアプリ側のセッションが別の認証で動き続けたことです。手元で Claude を使えている限り、ログインは生きていると思い込めます。10月13日にターミナルで claude を起動し /login をやり直して復旧し、止まった 3 ジョブは手動で再実行しました。
気づいたのは止まってから 2 日後の 10月13日です。やることの棚卸しをしたついでに各ジョブの日付つきログを見たところ、10月11日以降の実行がすべて同じエラーで終わっていました。
公式ドキュメントでは、保存されたログインが期限切れで更新できなくなると、各リクエストが「Login expired · Please run /login」で失敗すると説明されています。期限の 3 日前には起動時に警告が出ますが、起動時の警告は対話セッションで目に入るもので、無人実行では誰も見ません。同じページに、無人で動くセッションはログインが切れると復旧できないという趣旨の記述もあります(出典:Authentication、2026年10月時点)。
もう一点、claude -p の中では /login のようなターミナル専用コマンドは使えません(出典:Run Claude Code programmatically、2026年10月時点)。直すには人間がターミナルを開くしかないので、切れたことを早く知る仕組みが要ります。
setup-token が 401:トークン方式を諦めた
公式ドキュメントは、CI やスクリプト向けに claude setup-token で 1 年有効のトークンを出し、環境変数 CLAUDE_CODE_OAUTH_TOKEN に設定する方法を案内しています。Pro・Max・Team・Enterprise のプランが対象です(出典:同上 Authentication)。
筆者の環境では、2026年8月17日に発行したトークンがサーバー側で即 401 になり、curl で API を直接叩いても同じでした。原因までは突き止めていません。トークンは 109 字で、端末からコピーすると 101 字目に改行が混ざる点も記録に残っています。コピー時の混入は別の事故の種になるので、貼り付けたあとに文字数を数えると防げます。結局、トークンは使わずキーチェーン認証で動かす方式に切り替えました。
キーチェーンのログインを使う運用なら、--bare は付けないでください。公式ドキュメントによると、bare モードは OAuth 資格情報もシステムのキーチェーンも読まず、ANTHROPIC_API_KEY か apiKeyHelper を前提にします。CLAUDE_CODE_OAUTH_TOKEN も読まれません。
ファイル数上限 256:成功する週があるから見逃す
launchd から起動したジョブで、ファイル数の既定の上限(soft 256)が原因とみられる「low max file descriptors」というエラーが出て、断続的に落ちました。成功する週もあるので、一度動いたから大丈夫という判断が通ってしまいます。
入口スクリプトの先頭に ulimit -n 10240 を入れて対処しました。ただし、効いたかどうかはエラー文からの推定で、まだ実証していません。launchd のジョブごとの上限は、設定ファイル(plist)の SoftResourceLimits / HardResourceLimits にある NumberOfFiles で変えられると、macOS 付属のマニュアル(man launchd.plist)に書かれています。この Mac で launchctl limit maxfiles を実行すると、soft 256・hard unlimited と表示されました(2026年10月19日確認)。
スリープ中の起動:25 分の制限が 7.5 時間になった
日曜夜のジョブが、Mac のスリープ中に一瞬だけ起動して失敗しました。timeout はスリープ中の時間を数えないため、25 分で切るつもりの制限が、実時間では 7.5 時間かかった週があります。週次の振り返りノートが 2 週続けて作られませんでした。
制限時間を設けていても、時計が止まる環境では当てになりません。実行時刻をスリープしない時間帯に寄せるか、起動時に成果物の更新時刻を確かめる確認を入れるのが現実的です。
TCC の権限制限:デスクトップとドキュメントに触れない
launchd から ~/Desktop や ~/Documents を読み書きしようとすると、macOS の権限制限(TCC)で「Operation not permitted」になりました。回避策は 2 つです。
- 参照資料はホームの直下に複製してから読む
- 書き込みは python3 を経由して、一時置き場に逃がす
ターミナルで手動実行したときは同じ処理が通るので、定期実行にして初めて見つかります。このブログの記事生成パイプラインも、参照資料をホームに置いて読む方式に変えました。
健全性の見方:終了コードではなく日付つきログと更新時刻
launchctl list に出る終了コードと、追記専用の err.log は、古い失敗が残っていて当てになりません。筆者は実際にこれで誤診しました。
代わりに見るのは次の 2 点です。
- 日付つきログの
rc=行。実行のたびに別ファイルへ、結果の行を書き出す - 成果物の更新時刻。週次のはずのファイルが先週のままなら、止まっている
入口スクリプトの末尾で、次のように 1 行残すだけでも見え方が変わります(書き方の一例です)。
claude -p "..." > "$LOG" 2>&1
rc=$?
echo "$(date '+%F %T') rc=$rc" >> "$LOG"
公式ドキュメントでは、claude -p は成功時に終了コード 0、失敗時に 0 以外で終わるとされています。認証が無いといった実行中の失敗は、結果として標準出力に出力されます(出典:Run Claude Code programmatically、2026年10月時点)。ただし、これはコマンド単体の話です。ラッパーのスクリプトが途中で別のコマンドの終了コードを返したり、launchd 側の表示が古かったりすれば、結果の行で確かめるまで本当の状態は分かりません。
無人実行を切り替えるときは、権限確認の扱いも決めておく必要があります。--permission-prompts none を付けると、確認が必要な操作は拒否され、Claude に「承認できる人がいない」と伝わって実行が続きます(v2.1.259 以降、同上)。
FAQ
launchd と cron のどちらで回すのがよいですか
Mac で動かすなら、筆者は launchd を使っています。どちらでもスリープと権限制限の影響は受けるので、選択より上の見張りの設計のほうが効きます。
ログインが切れる前に気づく方法はありますか
公式ドキュメントには、/status のログイン行で期限切れ状態を確認できると書かれています(v2.1.210 以降)。無人実行の前に手動で確認する運用にするか、成果物の更新時刻を見張ってください。
API キーに切り替えれば解決しますか
キーチェーンのログイン切れは避けられますが、課金の仕組みが変わります。サブスクリプションの枠で使いたい場合は、この記事のログイン方式を前提に監視を整えるのが近道です。料金の実測は Claude Code の実際の料金に、5 時間の枠で止まる話は レート制限の記事にまとめています。
まとめ:作る前に、止まったことを知る手段を用意する
定期実行で時間を食ったのは、スクリプトを書く作業ではなく、止まっていたことに気づくまでの遅れでした。入口で日付つきのログを残し、週に一度は成果物の更新時刻を確かめる。それだけで、ログイン切れや 401 のような症状も早く拾えます。

コメント