普段は問題なく動いていた自動処理が、ある朝だけ次のメッセージで止まっていました。
fatal: detected dubious ownership in repository at '/tmp/repos/my-app'
To add an exception for this directory, call:
git config --global --add safe.directory /tmp/repos/my-app
2014年から個人開発を続けるなかで、いまは4つのサイトを Claude Code のスケジュール実行で毎日更新しているのですが、サンドボックス側のユーザーが切り替わったタイミングで、既存のクローンに対する git pull がこの一行で全部こけました。エラー文の親切さに反して、初見だと「なぜ昨日まで動いていたものが急に?」と戸惑うところです。原因と対処を、実際にぶつかった順に整理します。
なぜ「dubious ownership」が出るのか
このエラーは git のバグでも、リポジトリの破損でもありません。リポジトリのディレクトリ所有者と、いま git を実行しているユーザーが一致しないときに、git が安全のためにわざと処理を止めているだけです。
背景には CVE-2022-24765 という脆弱性への対応があります。共有マシンや一時ディレクトリ(/tmp など)に他人が置いた .git/config を、自分の権限で git が読み込んでしまうと、core.fsmonitor のような設定経由で任意コマンドを実行されかねません。そこで Git 2.35.2 以降は、所有者が違うリポジトリに触れた瞬間に止まる挙動になりました。
私の環境で起きたのは、まさにこのパターンです。所有権を確認すると一目瞭然でした。
$ ls -ld /tmp/repos/my-app /tmp/repos/my-app/.git
drwxr-xr-x 7 nobody nogroup 4096 /tmp/repos/my-app
drwxr-xr-x 8 nobody nogroup 4096 /tmp/repos/my-app/.git
$ id -un
runnerディレクトリは nobody 所有なのに、いま動いているのは runner ユーザー。前回の実行と今回の実行でサンドボックスのUIDが変わると、コンテナや一時環境では普通に起こります。
いちばん速い対処:safe.directory に登録する
そのリポジトリを信頼してよいと自分が判断できるなら、エラーメッセージが提示するとおり、例外として登録するのが最短です。
git config --global --add safe.directory /tmp/repos/my-appこれで該当ディレクトリだけが許可リストに入り、以降の git pull や git status が通ります。設定は実行ユーザーのグローバル ~/.gitconfig に [safe] セクションとして追記されるので、中身を確認したいときは次のとおりです。
$ git config --global --get-all safe.directory
/tmp/repos/my-appポイントは --add を付けること。--add なしで git config --global safe.directory ... を繰り返すと、複数リポジトリを扱うときに前の値を上書きしてしまいます。私は自動化スクリプトの冒頭で、扱うリポジトリのパスをそのまま --add で登録するようにしてから、この手のつまずきがなくなりました。
ワイルドカード指定は便利だが範囲に注意
毎回パスを足すのが面倒な場合、ワイルドカードですべてのリポジトリを信頼する書き方もあります。
git config --global --add safe.directory '*'CI のように「このコンテナの中身は自分の管理下で、毎回作り直される使い捨て環境」だと割り切れるなら、これは実用的です。私自身、使い捨てのビルドコンテナでは * を使っています。
ただし共有開発マシンや、他人のリポジトリが混ざりうる環境では * を避けてください。所有者チェックという防御を丸ごと外すことになり、CVE-2022-24765 が想定したリスクがそのまま戻ってきます。使い捨てかどうかを基準に選ぶのがちょうどよい線引きだと考えています。
「dubious ownership」と「Permission denied」は別物
ここが、私が同じ朝に踏んだ一番の落とし穴でした。safe.directory を登録したのに、今度は次のエラーに変わったのです。
error: cannot open .git/FETCH_HEAD: Permission denied
これを見て「safe.directory の設定が効いていない」と勘違いしがちですが、別の問題です。整理すると次のようになります。
detected dubious ownership… 所有者が違うので git が自主的に止めている。設定(safe.directory)で解除できるPermission denied(FETCH_HEAD などの書き込み時)… OSのファイルパーミッション上、本当に書き込めない。設定では解決しない
nobody 所有のディレクトリに runner ユーザーが git pull で書き込もうとすれば、safe.directory をいくら足してもOSが弾きます。ls -l で実際の権限を見て、書き込みビットが自分にあるかを確認するのが確実です。
$ touch /tmp/repos/my-app/.git/_wtest && echo OK || echo "NOT WRITABLE"
NOT WRITABLE書き込めないと分かったら、設定をいじるフェーズではありません。所有権そのものを直すか、書ける場所に移すフェーズです。
環境に応じた根本対処
所有権の不一致を恒久的に解消するには、状況に合わせて次のどれかを選びます。
所有権を取り戻せる権限があるなら、chown で実行ユーザーに揃えるのが王道です。
sudo chown -R "$(id -un):$(id -gn)" /tmp/repos/my-appsudo が使えない、あるいは所有者を変えてはいけない共有環境なら、自分が確実に書ける場所に作り直すのが堅実です。私のスケジュール実行では、/tmp 配下が別ユーザー所有で書けなくなるケースが定期的に起きたため、書き込み可能な $HOME 配下へフォールバックする形に切り替えました。
WORK="$HOME/repos/my-app"
if [ -d "$WORK/.git" ] && [ -w "$WORK/.git" ]; then
cd "$WORK" && git pull --rebase origin main
else
rm -rf "$WORK"
git clone --depth 1 "$REPO_URL" "$WORK"
fiクローン自体を、最終的に git を動かすユーザーと同じユーザーで行うのも効果的です。Docker でビルド中に root でクローンし、実行時に非rootユーザーに切り替える構成だと、まさに所有権の食い違いが生まれます。USER を切り替えた後にクロードする、もしくは chown を一手間挟むだけで未然に防げます。
同じ朝を二度繰り返さないために
このエラーは、根本的には「誰がそのファイルを所有しているか」という一点に集約されます。自動化の冒頭で所有権と書き込み可否を先に確認しておくと、止まってから原因を探すより圧倒的に速いです。私は次の3行をスクリプトの先頭に置くようにしました。
git config --global --add safe.directory "$WORK"
[ -w "$WORK/.git" ] || WORK="$HOME/repos/$(basename "$WORK")"
echo "owner=$(stat -c '%U' "$WORK" 2>/dev/null) me=$(id -un)"まず safe.directory を登録して所有者チェックを通し、書けなければ書ける場所へ逃がし、最後に所有者と自分を並べて記録しておく。たったこれだけで、翌朝ログを開いたときに何が起きたかが一目で分かります。
毎日動かす仕組みほど、こういう静かな前提のズレが効いてきます。同じところでつまずいている方の助けになれば幸いです。