Claude Codeのsandboxでgit addが失敗する原因と回避策
Linuxのsandbox内でgit add .がexit 128で止まり、存在しないファイルがgit statusに並ぶ不具合の仕組みと、Issueで確認された範囲、今できる回避策をまとめます。
Linuxでsandboxを有効にしたClaude Codeにgit add .を実行させると、fatal: adding files failedで止まり、exit 128でステージされるファイルは1つもありません。同じ場所でgit statusを打つと、手元に存在しないはずの.zshrcや.mcp.jsonが未追跡ファイルとして並びます。
原因は、sandboxが保護パスの位置に置く「placeholder」です。GitHubのIssue #78419は、これをClaude Code側の不具合として扱っています。この記事では、再現手順、placeholderの正体、今日できる回避策を順に説明します。
git add .が止まるとき、何が起きているのか
Issueの報告者が再現したのは、Claude Code 2.1.212、Linux、bubblewrap、sandboxはauto-allowモード、通常のgit cloneという環境です。worktreeでもdenyReadルールでもありません。
手順は3つです。
.zshrc、.mcp.json、.claude/hooksがないgitリポジトリのルートでClaude Codeを起動するgit status --shortを実行させると、手元にない約19個のパスが未追跡として出るgit add -n .を実行させると、次のエラーで止まる
error: .bash_profile: can only add regular files, symbolic links or git-directories
fatal: adding files failed
(exit 128)ls -la .zshrcの結果はcrw-rw-rw- 1 nobody nobody 1, 3、つまり/dev/nullと同じキャラクタデバイスでした。gitは通常ファイル、シンボリックリンク、gitディレクトリしか追加できません。1つでもデバイスファイルが混じると、git add .は途中で諦めるのではなく全体を中止します。
見落としやすいのは、止まるのが「怪しいファイル」だけではない点です。正当な変更ファイルも、同じコマンドの中で一緒にステージされなくなります。
placeholderはなぜ作られるのか
sandboxはもともと、設定ファイルへの書き込みを禁じます。コマンドが.mcp.jsonや.claude/hooksを書き換えれば、Claude Codeがsandbox外で実行するhookやMCPサーバーを差し替えられるからです。sandboxingのページは、カレントディレクトリでは次を保護すると説明しています。
.bashrcや.zshrcなどのシェル起動ファイル.gitconfig.vscodeと.ideaディレクトリ.git内のhooksとconfig.mcp.jsonと.claude/hooks
保護の対象が実在するファイルなら、読み取り専用でbindされるだけで、痕跡は残りません。問題は、対象がまだ存在しないときです。
Claude Codeのメンテナーはコメントで、このときLinuxのsandboxが保護パスを「placeholderで覆う」と説明しています。placeholderはsandbox内からは幽霊のようなデバイスファイルに見え、gitがそれを拒否した結果がgit add .の中断です。書き込み禁止そのものは意図した仕様で、git add .が壊れることとエージェントが混乱することは意図していない、というのがメンテナーの結論でした。
Issueの報告者は範囲も測っています。placeholderが現れるのは起動ディレクトリだけです。permissions.additionalDirectoriesで追加したパスと、起動ツリー内の入れ子のリポジトリには現れませんでした。保護パスが存在しない場所にだけ出て、実在するパスは痕跡を残しません。
数と中身はバージョンで変わる
報告者の約19個は2.1.212での数字です。メンテナーが2.1.233で再現した結果は11個でした。
| 観測 | バージョン | 件数 | 例 |
|---|---|---|---|
| Issue本文 | バージョン2.1.212 | 件数約19 | 例.zshrc、.mcp.json、.claude/hooksなど |
| メンテナーの再現 | バージョン2.1.233 | 件数11 | 例.bashrc、.zshrc、.gitconfig、.gitmodules、.profile、.zprofile、.bash_profile、.ripgreprc、.mcp.json、.vscode、.idea |
2.1.233の再現は、コミット済みファイルが1つだけの新しいリポジトリで行われました。件数は環境で変わるので、自分の環境で何個出るかはgit status --porcelainで数えるのが確実です。
sandboxingのページとは何が食い違うのか
sandboxingのトラブルシューティングには、.claude設定パスに0バイトの読み取り専用ファイルが現れる節があります。そこでは、まだ存在しないファイルへの書き込み拒否を保つため、sandboxコマンドの実行中だけ0バイトのplaceholderを作り、終わると消すと書かれています。セッションが強制終了されてplaceholderが残った場合は、claude doctorで一覧できます。
この説明とIssueの観測は、同じplaceholderの別の側面です。Issueは、同じスタブがsandbox内ではキャラクタデバイス、ホスト側では0バイトの通常ファイルに見えると指摘しています。Issueを書いた人は、この二重の見え方こそが、sandbox-runtime側の議論で2つの陣営が噛み合わなかった原因だと見ています。
一方で、Issueの本文は「sandboxingのページにはスタブファイルもgit statusのノイズも書かれていない」と述べています。ページを見るかぎり、git add .が止まる症状と結びつけた説明は、保護パスの節にもトラブルシューティングにも載っていません。
自分の環境が該当するか確かめる
該当するかは、sandbox内のBashで2つのコマンドを実行させれば分かります。git status --porcelainに出る未追跡パスのうち、自分で作った覚えのない.zshrcや.ideaの数を見ます。次に、その1つにls -laを打ち、先頭がcのキャラクタデバイスになっていれば、この記事の症状です。
git status --porcelain
ls -la .zshrcsandboxの外、つまり別のターミナルで同じgit statusを打てば、これらのパスは出ません。両方で結果が食い違うなら、リポジトリではなくsandboxが原因です。食い違わず、実在するファイルなら別の問題なので、通常のgitの調査に戻ります。
回避策: ステージするパスを明示する
Issueが挙げる回避策は、常にパスを明示してステージすることです。ただし、エラーに当たるまで気づけない点を報告者は問題にしています。
# 失敗する
git add .
# 通る形(変更したファイルを列挙する)
git add src/app.ts README.mdClaude Codeに任せるなら、CLAUDE.mdに1行書いておくと、エラーで1往復する手間が減ります。次は書き方の一例です。
## Git
- sandbox内では `git add .` と `git add -A` を使わない。ステージするパスを明示する
- `git status` に `.zshrc` `.mcp.json` など実在しないドットファイルが出ても、
.gitignore へ追記しない(sandboxが作る一時的な表示)2行目は、Issueが「エージェントが自然に取る悪い対処」として挙げた点を避けるための記述です。見慣れないファイルが並ぶと、エージェントは.gitignoreへの追記を提案しがちです。報告者によれば、これはマウントポイントの一覧をリポジトリに永久に書き込む行為になります。公開のdotfilesリポジトリなら、誰にも見えないsandboxの内部事情を公開することにもなります。
dotfilesリポジトリでは逆向きの危険がある
.zshrc、.bashrc、.gitconfigが成果物そのものであるdotfilesリポジトリでは、さらに厄介です。「この名前はsandboxのノイズ」とエージェントに教え込むと、本物の変更まで無視しかねません。上のCLAUDE.mdの記述は、dotfilesリポジトリでは使わないでください。そこでは、git diffで実際の差分があるかを先に確かめる運用のほうが安全です。
保護そのものを外す手段は広すぎる
placeholderを消す目的で保護を外す選択肢は、sandboxingのページを見るとほぼありません。保護パスはallowWriteでもEditの許可ルールでも解除できず、外せるのはfilesystem.disabledでファイルシステム分離ごと止めるときだけです。ネットワーク分離は残りますが、ファイル分離が全パスで消えます。仕組みはsandbox.filesystem.disabledはファイルシステム分離だけ外す設定で扱っています。書き込み範囲を広げる設定はallowWrite/denyWriteでkubectl・terraformの書き込み範囲を調整するにありますが、保護パスには効きません。
git add .1つのためにファイル分離を捨てる価値があるかは、運用ごとの判断です。ほとんどの場合、パスの明示で足ります。
Issueの経緯とメンテナーの方針
このIssueは2026年7月17日に立てられ、bug、platform:linux、area:sandbox、reproducedのラベルが付いています。2026年8月17日のメンテナーのコメントは、最新リリースでも再現するバグだと認め、gitの内部パスについては同様の処置が済んでいると述べました。内部パスのマスクはgit statusを汚さなくなったので、残る設定ファイル系のマスクにも同じ対応を検討している、という内容です。
Issueの時点では、修正の予定バージョンは示されていません。報告者が挙げる修正案は優先順に4つあります。
- 作業ツリー内にマウント先を作らない(根本対処。sandbox-runtimeのIssue #139で追跡)
- sandbox内のコマンドに、スタブのパスを環境変数やsystem-reminderで伝える
- sandbox内だけ
.git/info/excludeにスタブのパスを足し、gitから隠す(git add .も復旧する) - sandboxingのページに挙動を書く
コメント欄の別の利用者は、4番の文書化は根本修正が入らない限り必須だと述べています。jj(Jujutsu)でワーキングコピーのスナップショットを自動で取る環境では、スナップショットとコミットの書き換えが重なって競合状態になったという報告もありました。作業ツリーに触れる副作用は、gitだけに影響するとは限りません。
関連する過去の経緯もあります。worktree版の不具合(#29316)は2.1.78で修正済みとされ、通常のcloneで同じ症状を報告した最初のIssue(#17087)は、69件の賛成と26件のコメントがついたまま閉じられました。報告者は今回のIssueを、その後継として位置づけています。
同じsandboxで別の症状に当たったら
sandboxの不具合は症状ごとに原因が違います。unshareが断続的に失敗するならunshare(CLONE_NEWUSER)がClaude Codeのサンドボックスで失敗する原因を、sandbox.enabledの基本挙動はsandbox.enabledはClaude CodeのBashコマンドを隔離する設定を参照してください。
まとめ
git add .が止まる原因はgitの設定でもリポジトリの状態でもなく、sandboxが保護パスに置くデバイスファイル風のplaceholderです。Linuxで保護パスの実ファイルがないときに起き、起動ディレクトリにだけ現れます。直るまでは、ステージするパスを明示し、.gitignoreへ追記しない運用にしておけば、作業は止まりません。dotfilesリポジトリだけは、ノイズと本物の変更の区別を人間が持つ必要があります。