CC for Biz
2026/06/08Claude Code
導入・運用

Macのgit自動コミットが止まる原因|launchd権限の罠

Macのgit自動コミットが止まる原因|launchd権限の罠

「毎日23時に動いているはずのgit自動コミットが、気づいたら2週間以上止まっていた」——Macで自動化を組んだことがある人なら、誰もが一度はこの種の不安にぶつかります。

結論から言うと、原因はmacOSのプライバシー保護(フルディスクアクセス)と、スクリプト自身のログ書き込みにありました。しかも問題は3層に分かれていて、1つ直してもまだ動かないという厄介な構造でした。

株式会社Fyveは、日々の開発リポジトリを毎日自動でコミット・バックアップする仕組みをMacのlaunchdで運用しています。本記事では、その自動コミットが実際に止まったときの切り分け手順と対処を、エンジニア・Macで自動化を組む方向けにそのまま公開します。

何が起きたか — 18日間ノーコミットの発覚

私が運用しているのは、毎日決まった時刻に複数のgitリポジトリの未コミット変更を自動でコミットし、リモートへpushするシェルスクリプトです。これをmacOSのlaunchd(常駐のジョブ管理機能)で毎晩23時に起動しています。

ある日ログを確認したところ、最後にコミットが成功したのは18日前。つまり半月以上、自動バックアップが完全に止まっていました。ローカルでの変更は17コミット分たまっていて、リモートへは一度もpushできていない状態です。

調べていくと、問題は1つではありませんでした。表面的な停止・隠れた起動失敗・それ以前からの長期バグの3層が重なっていたのです。順番に見ていきます。

自動コミットが止まった3層の原因マップ(Operation not permitted・EX_CONFIG・cannot rebaseと各対処)

原因① launchdからスクリプトが動かない「Operation not permitted」

まず確認したのは、launchdのジョブ状態です。ターミナルで状態を見ると、ジョブの最終終了コードが78になっていました。標準エラー出力のログには、次の1行が毎晩くり返し記録されていました。

  • /bin/bash: ...your-repo/scripts/autocommit.sh: Operation not permitted

「Operation not permitted(操作は許可されていません)」——これはmacOSのTCC(Transparency, Consent, and Control)、いわゆるプライバシー保護による拒否です。リポジトリとスクリプトが ~/Desktop(デスクトップ)配下にあったため、ここにアクセスできなくなっていました。

なぜDesktopやDocuments配下が危ないのか

macOSは、デスクトップ・書類・ダウンロードといったフォルダを保護対象にしています。普段ターミナルで自分が叩くコマンドは問題なく動きますが、launchdから起動されたプロセスは、ユーザーがGUIで操作しているときのアクセス権を自動では引き継ぎません

そのため、launchd経由で起動したbashが保護フォルダ内のスクリプトを読もうとした瞬間に拒否され、ジョブは毎晩発火しては即座に死んでいました。OSのアップデートやプライバシー設定のリセットを境に、ある日突然この状態になることがあります。

対処はシンプルで、スクリプトを実行するバイナリ(この場合は /bin/bash)にフルディスクアクセスを付与します。システム設定 →「プライバシーとセキュリティ」→「フルディスクアクセス」を開き、「+」から /bin/bash を追加します(ファイル選択画面で ⌘⇧G を押し、/bin と入力すると選択できます)。

ただし /bin/bash 全体にフルディスクアクセスを与えるのは付与範囲が広い点に注意してください。範囲を絞りたい場合は、このジョブ専用の小さな実行ファイルを用意し、それにだけ権限を与える方法もあります。

原因② 権限を付けても動かない「EX_CONFIG(78)」の罠

ここが今回いちばんハマったポイントです。フルディスクアクセスを付けてジョブを再起動しても、まだ動きませんでした。終了コードは相変わらず78(EX_CONFIG=設定エラー)。しかも今度は標準エラーが空で、ログには起動の痕跡すら残りません。スクリプトが一行も実行されていないのです。

原因を切り分けるため、最小のプローブ用LaunchAgentを作りました。中身はほぼ同じ /bin/bash ですが、出力先(標準出力・標準エラーのリダイレクト先)だけを /tmp に変えたものです。これを手動で起動して確認すると、結果は次の通りでした。

  • bash自体は起動している
  • ~/Desktop 配下への書き込みも成功している
  • スクリプトの読み込みも成功している(終了コード0)

つまりフルディスクアクセスは正しく効いていました。本番ジョブとプローブの唯一の違いは、launchdの出力先(StandardOutPath/StandardErrorPath)が ~/Desktop 配下にあることだったのです。

launchdがリダイレクト用のログファイルを開く処理は、bashに付与したフルディスクアクセスではカバーされません。出力先を保護フォルダ内に指定していると、launchdはそのファイルを開けず、bashを起動する前に設定エラー(EX_CONFIG/78)でジョブを終了させます。だからログにも標準エラーにも何も残らない——という、原因が見えにくい挙動になります。

同じ終了コード78でも原因が違う理由:bashが開くファイルはFDAでカバーされるが、launchd自身が開く出力先はカバーされない

対処は、launchd自身の出力先をDesktop外に移すことです。~/Library/Logs/ はTCCの保護対象外で、ログの置き場所として標準的です。plistのStandardOutPath/StandardErrorPathをここに書き換えて再読み込みしたところ、ようやくジョブが最後まで走り、終了コードが0になりました。

なお、スクリプトが自前で書き出すログ(autocommit.log など)はDesktop配下のままで問題ありません。そちらはフルディスクアクセスを付けたbashが書くため、①の対処でカバーされています。「launchdが開くファイル」と「スクリプトが開くファイル」で権限の経路が違う、という理解がポイントです。

原因③ pushが毎回失敗する「cannot rebase: unstaged changes」

①②を直してジョブは動くようになりましたが、実はそれ以前から、コミットはできてもpushだけが毎回失敗していました。ログには次のように出ます。

  • error: cannot rebase: You have unstaged changes.

これはTCCとは無関係の、スクリプト設計に潜んでいた長期バグでした。スクリプトはコミット後・rebase前に、git管理下に置かれたログファイルへ進捗を書き込んでいました。その書き込みで作業ツリーが汚れ、直後の git rebase が「未ステージの変更がある」として中断し、pushがスキップされていたのです。

ログ上は rebase failed (conflict) と表示されていましたが、実体はコンフリクトではなく自分自身のログ書き込みによる作業ツリーの汚れでした。表示メッセージに引きずられると原因を見誤る典型例です。

対処は単純で、ログ用ディレクトリを .gitignore に追加し、git追跡からも外すだけです。ログがgitを汚さなくなり、rebaseが通り、pushが成功するようになりました。そもそも実行ログをバージョン管理下に置いていたことが根本原因だったので、これが正しい形でした。

切り分けの考え方 — 「症状が同じでも原因は別」

今回いちばんの学びは、終了コード78が3度出たが、その中身は別々だったことです。同じエラー番号でも、①はバイナリのアクセス権、②はリダイレクト先のアクセス権、と原因が異なります。症状の文字列だけで判断すると沼にはまります。

こういうときに効くのが、変数を1つずつ潰すプローブです。本番とほぼ同じ条件で「出力先だけ」を変えたミニジョブを作って動かせば、原因が「スクリプト本体」なのか「launchdの出力先」なのかを一発で切り分けられます。推測で設定をいじる前に、まず切り分けの実験を1つ挟むのが結局いちばん速い、というのが現場の実感です。

3つの原因と対処を整理すると次の通りです。

  • ① Operation not permitted:保護フォルダへのアクセス拒否 → 実行バイナリ(/bin/bash)にフルディスクアクセス
  • ② EX_CONFIG(78)・ログ無し:launchdの出力先が保護フォルダ → 出力先を ~/Library/Logs/ などへ移動
  • ③ cannot rebase: unstaged changes:ログのgit追跡で作業ツリーが汚れる → ログを .gitignore +追跡解除

同じ轍を踏まないためのチェックリスト

Macで定期実行の自動化(git自動コミット・自動バックアップ・cron的なジョブ)を組むときに、私が今回得た教訓は次のとおりです。

Macで定期実行の自動化を組むときのチェックリスト:FDA・出力先・ログのgit管理・失敗検知
  • リポジトリを ~/Desktop~/Documents 配下に置くなら、自動化ジョブの実行バイナリにフルディスクアクセスを付ける前提で設計する
  • launchdのStandardOutPath/StandardErrorPathは、保護フォルダの外~/Library/Logs/ など)に置く
  • スクリプトが書き出す実行ログは、git管理に入れない(rebase・commitを汚す)
  • 自動化は「組んで終わり」にせず、失敗を検知する仕組み(失敗時のmacOS通知、最終成功日時の監視など)をセットで用意する

特に最後の項目は重要です。今回は18日も止まっていたことに気づくのが遅れました。自動化そのものより、「止まったことに気づける設計」のほうが運用では効いてきます。

こうした日々の自動化を、AIエージェントに任せて回す具体例については、こちらの記事でも触れています。

Claude Codeでコンテンツ生成を自動化した全手順

まとめ

Macのgit自動コミットがlaunchdで止まる問題は、多くの場合、フルディスクアクセス・launchdの出力先・ログのgit管理という3層のどこか(あるいは複数)に原因があります。同じ終了コードでも中身が違うため、プローブで1変数ずつ切り分けるのが近道です。

私たちは、こうした泥臭い自動化の運用ノウハウまで含めて、中小企業のAI活用と業務自動化を支援しています。「自動化を組んだものの、止まっても気づけない」「Macで動かしているスクリプトの管理が属人化している」といった段階から、株式会社Fyveは伴走してご支援します。

「Claude Code を自分で使いこなしたい」「自社の業務に組み込みたい」
── そんな方は、まず初回無料相談でお話ししてみませんか。

← 記事一覧に戻る

御社の業務に合わせたClaude Code導入支援

「AIツールを導入したが、現場で使われない」を終わらせる。
業務課題のヒアリングから設計、ハンズオン実践、運用定着まで一貫して支援します。

無料AI活用診断を受ける料金とサービス一覧を見る →
© 2025 Fyve Inc. All rights reserved.