Claude CodeのEnterWorktreeでBashが全滅——bwrap「Read-only file system」の正体と回避策
Claude CodeでEnterWorktreeツールを使うと以降のBashが全滅し「bwrap: Read-only file system」で落ちる不具合を、公式worktreeドキュメントとGitHub Issue群から検証。原因の仕組みと回避策をまとめた。
エンジニアのゆとです。
Linux(WSL2含む)でサンドボックスを有効にしたままClaude CodeにEnterWorktreeツールでworktreeへ移動させると、以降のBashが全滅する不具合がGitHub Issueに上がっている。エラーはこれだ。
bwrap: Can't create file at <repo>/.claude/worktrees/wt1/.gitconfig: Read-only file system
厄介なのは、worktreeの作成自体は成功しているのに、そのあとのgit commitはおろかpwdすら通らなくなる点。セッションが「詰む」感覚に近い。
自分はふだんmacOSのSeatbelt環境で作業しているのでこの不具合を直接踏んではいないけど、CIやVPS上でLinuxのサンドボックス運用をしている知り合いのエンジニアからも「worktree絡みでBashが固まる」という話を聞いたことがあったので、今回はIssueと公式ドキュメントを突き合わせて中身を整理しておく。
症状の再現手順
Issue #95949にある再現コードを要約するとこうなる。
export CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1
git init demo && cd demo
mkdir -p .claude/worktrees
echo '# demo' > README.md
git add README.md .claude && git commit -qm init
git worktree add -q -b old1 .claude/worktrees/old1 # 既存のworktreeが1つある状態
claude -p "Step1: bashでecho warmを実行。Step2: EnterWorktreeツールでwt1という名前のworktreeを作って入る。Step3: pwd; touch probe.txt; git add probe.txt && git commit -qm probeを実行" \
--model haiku --allowedTools EnterWorktree,Bash --output-format stream-json --verbose
ポイントは2つ。
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1という環境変数でサンドボックスを起動していること(settings.jsonのsandbox.enabledではない)- セッションの最初にBashを1回実行してから、あとになってEnterWorktreeで新しいworktreeに入っていること
報告者の環境(Claude Code 2.1.263、Ubuntu 24.04)では、Step1のBashとStep2のworktree作成までは成功する。ところがStep3以降、すべてのBashが例外なく次のエラーで落ちる。
bwrap: Can't create file at <repo>/.claude/worktrees/wt1/.gitconfig: Read-only file system
さらに2.1.278では症状の出方が変わり、失敗するタイミングが前倒しになる。EnterWorktreeの実行そのものが.git/worktrees/<name>を書き込み禁止扱いされてブロックされ、セッションは「作られていないworktreeを指す壊れたGIT_DIR」を抱えたまま止まる。バージョンが上がってバグが直るどころか、失敗の場所が移動しただけという状態だ。
公式ドキュメントは「worktreeの書き込みは許可される」と明言している
まずここが一番大事なポイントなんだけど、Claude Codeの公式worktreeドキュメントには、サンドボックスとworktreeの関係についてはっきりした記述がある。
The repository’s
.gitdirectory: git commands in a worktree write to the main repository’s shared.gitdirectory, and sandboxing allows those writes, so commands such asgit commitwork from inside a worktree with the sandbox enabled.
要するに「worktreeの中でgit commitしても、共有の.gitディレクトリへの書き込みはサンドボックスが許可するように設計されている」と明言されている。これは後述する過去のバグ修正の結果として入った仕様のはずで、本来ならworktree×サンドボックスの組み合わせで詰まることはないはずなんだ。

ところが#95949が突いているのは、この「許可されるはず」の前提が、EnterWorktreeで後から作られたworktreeには適用されないケースがあるという点。ドキュメントの記述はあくまで一般論で、「セッション途中で新しく作られたworktree」という時間差のケースまではカバーしていない。
原因: マウントプランはセッション開始時に固定される
なぜこんなことが起きるのか。報告者の分析はこうだ。
bwrapサンドボックスは、セッションが最初にBashを実行するタイミングで「どのディレクトリを書き込み可能にするか」というマウントプランを組み立てる。このときリポジトリのルートは書き込み可能マウント、.claude/配下は基本的に書き込み禁止マウントとして扱われる(設定ファイルの改ざん防止のため)。
問題は、EnterWorktreeで新しいworktreeを作るのがこのマウントプラン構築の「あと」だということ。.claude/worktrees/wt1/はセッション開始時点ではまだ存在しない。マウントプランは静的に一度組まれるだけなので、あとから生えてきたworktreeディレクトリは「.claude/配下の書き込み禁止領域」という古い分類のまま取り残される。bwrapからすれば、存在しないディレクトリの中に.gitconfigを作ろうとしているのに、その場所は最初から読み取り専用としてマウントされている——というだけの話になる。
これはCLAUDE_CODE_SUBPROCESS_ENV_SCRUBという環境変数経由でサンドボックスを起動した場合に固有の症状らしい。報告者が本文で明記している通り、settings.jsonベースのサンドボックス設定に切り替えると、同じ操作が問題なく通る。
Launching the same sessions with the settings-based sandbox instead of the env var —
--settings '{"sandbox":{"enabled":true,"allowUnsandboxedCommands":false,"failIfUnavailable":true}}'— passes the identical sequence on both versions: worktree writes and git commits succeed, while out-of-session writes … stay denied, with zero lingering bwrap mounts.
つまり同じ「bwrapでサンドボックスする」という結果でも、起動経路(環境変数 vs settings.json)によってマウントプランの再構築ロジックが違う、ということが実測で示されている。設定ベースのエンジンはworktree作成後もマウントプランを追従させられているのに対し、環境変数スクラブ経由のエンジンはセッション開始時点のスナップショットのまま止まっている、という見立てだ。
この不具合、実は7ヶ月前から同じ形で繰り返されている
自分が今回この記事を書こうと思った理由がここにある。「サンドボックスがworktreeがらみでRead-only file systemを吐く」というパターン自体は、#95949が初出じゃない。
#26262(2026年2月)は、そもそもworktreeが参照する共有.gitディレクトリ自体がサンドボックスの書き込み許可の対象外になっていて、git commitがUnable to create '.../index.lock': Read-only file systemで落ちるという報告だった。これが先に紹介した「worktreeでのgit commitは許可されるはず」という公式ドキュメントの記述の土台になった修正のはずで、このケース自体は解消されている。
さらに遡ると、サンドボックスの初期化そのものが自分の書き込み禁止ルールに引っかかって全滅する系統のバグも2件見つかる。
#50781は「サンドボックス自身の書き込み禁止ルールが、サンドボックス自身の初期化ファイル作成とぶつかる」という自己矛盾、#77271は「.claude/settings.jsonがまだ存在しない新規プロジェクトで初回起動が自滅する」というケース。どちらも根っこは同じで、bwrapのマウントプランが「実際にその時点でファイルシステムがどうなっているか」と噛み合わなくなると、Read-only file systemという同じ形のエラーで全滅するという構造になっている。
今回の#95949は、この一連のバグ系統の最新版が「EnterWorktreeによる動的なworktree作成」という新しい引き金で再発した、という位置づけで見るのが実情に近いと思う。個々の修正は入っているはずなのに、マウントプランが静的である限り、新しい「あとから増えるパス」のパターンが出るたびに同じ形のエラーが顔を出す。
今すぐ試せる回避策
修正を待つ間の現実的な手はいくつかある。優先度順に並べる。
1. CLAUDE_CODE_SUBPROCESS_ENV_SCRUBではなくsettings.jsonでサンドボックスを有効化する
報告者自身が実測で確認している一番確実な回避策がこれ。
{
"sandbox": {
"enabled": true,
"allowUnsandboxedCommands": false,
"failIfUnavailable": true
}
}
環境変数経由の起動をやめて、settings.jsonのsandbox.enabledで有効化する。この方式ならEnterWorktreeで後発のworktreeを作っても、書き込みとgit commitが通り、かつ想定通り.gitconfigやセッション外の.claude/settings.jsonへの書き込みは拒否されたままだったと報告されている。CI設定でCLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1を機械的にexportしている場合は、まずここを疑う価値がある。

2. EnterWorktreeに頼らず、セッション開始前にworktreeを作っておく
そもそもマウントプランが固定されるのはセッション開始時のBash実行のタイミングなので、Claudeにセッション内でEnterWorktreeを呼ばせるのではなく、claude --worktree <name>で最初からworktree内にセッションを立ち上げるか、git worktree addで事前にworktreeを作ってからcdしてセッションを開始すれば、そもそも「あとから生えるパス」問題自体が発生しない。

並列タスクをサブエージェントに投げる運用なら、isolation: worktreeをフロントマターに書いてサブエージェント側にworktreeを持たせる設計のほうが、セッション途中でのEnterWorktree呼び出しより安定しやすい。
3. サンドボックスを切りたくないならmacOS(Seatbelt)側で検証する
今回の不具合はbwrap(Linux/WSL2)固有で、macOSのSeatbelt実装では報告が見当たらない。どうしてもEnterWorktreeを使った動的なworktree切り替えを試したいなら、まずmacOS環境で挙動を確認してから、Linux本番環境ではsettings.jsonベースのサンドボックス設定に切り替えるという二段構えが安全だと思う。
4. どうしても直らない場合はsandbox.filesystem.disabledは最終手段
CLAUDE_CODE_SUBPROCESS_ENV_SCRUBが設定されていると、Claude Codeはmanaged設定を含むあらゆるソースからのfilesystem.disabledを無視してファイルシステム隔離を維持し続ける仕様になっている。つまり環境変数経由のスクラブを使っている限り、ファイルシステム隔離だけを部分的に切るという抜け道は公式に塞がれている。詰んだらまず1の設定ベース方式への切り替えを検討したほうが早い。
自分の環境で切り分ける手順
worktree絡みでBashが動かなくなったら、この順番で切り分けるとよさそうだ。
- エラーメッセージに
bwrapとRead-only file systemの両方が含まれているか確認する。含まれていればこの記事の症状系統である可能性が高い - サンドボックスの起動経路を確認する。
CLAUDE_CODE_SUBPROCESS_ENV_SCRUBを環境変数でexportしているか、それともsettings.jsonのsandbox.enabledで有効化しているか - 環境変数経由なら、settings.jsonベースの
sandbox.enabled: trueに切り替えて同じ操作を再現してみる。これで通るなら原因はマウントプランの再構築ロジックの差でほぼ確定 claude --versionで現在のバージョンを確認する。2.1.263系と2.1.278系では失敗するタイミングが違う(前者はEnterWorktree後のBash全滅、後者はEnterWorktree自体のブロック)ので、症状の出方からおおよそのバージョン帯を推測できる
似たサンドボックス系トラブルと混同しないために
Claude Codeのサンドボックス周りは更新頻度が高く、似た時期に別種の不具合が重なることも多い。excludedCommandsに登録したはずのコマンドがサンドボックス内に取り残される別系統の不具合も、最近扱ったばかりだ。

「worktreeに入った途端Bashが死ぬ」のか「特定コマンドだけがサンドボックス判定から漏れる」のかで原因の層がまったく違うので、まずエラーメッセージにbwrapとRead-only file systemが出ているかどうかで切り分けるのが早い。
FAQ
macOSでも同じ現象は起きますか?
Issue #95949の報告はUbuntu 24.04(bwrap)に限定されている。macOSはSeatbelt、Linux/WSL2はbwrapと隔離の実装自体が別物なので、この記事の症状はLinux/WSL2固有と見ていい。
サンドボックスを無効にすれば回避できますか?
できるが、それはファイルシステムとネットワークの隔離を丸ごと諦めることになる。CLAUDE_CODE_SUBPROCESS_ENV_SCRUBを使っている場合はfilesystem.disabled自体が無視される仕様なので、無効化したいならまず環境変数経由の起動をやめてsettings.jsonベースに切り替えるところから始めることになる。
EnterWorktreeを使わなければ絶対安全ですか?
--worktreeフラグでセッション開始時からworktreeに入っている分には、マウントプランの構築時点でそのパスが存在するので、この記事で扱っている「あとから生えたパスが書き込み禁止のまま」という症状は原理的に起きにくい。ただしサンドボックス実装自体のバグは他にも報告例があるので、100%安全とは言い切れない。
まとめ
Claude CodeのEnterWorktreeツールでセッション途中にworktreeへ移動すると、Linux/WSL2のbwrapサンドボックス環境下でBashがRead-only file systemエラーで全滅する不具合が#95949で報告されている。原因は、bwrapのマウントプランがセッション開始時のBash実行タイミングで一度だけ組まれ、あとから作られたworktreeパスが古い「書き込み禁止」分類のまま取り残されること。公式ドキュメントは「worktreeでのgit commitはサンドボックスが許可する」と明言しているが、この前提はセッション開始前から存在するworktreeが対象で、セッション途中の動的な作成までは想定されていないように見える。
記事執筆時点(2026年9月22日、Claude Code 2.1.278)でこの不具合はまだオープンのままだ。同種の「サンドボックス×worktreeでRead-only file system」という形のバグは2月の#26262から数えて今回で少なくとも3回目の再発になる。実測で確認されている回避策は、CLAUDE_CODE_SUBPROCESS_ENV_SCRUBをやめてsettings.jsonのsandbox.enabledに切り替えるか、そもそもEnterWorktreeに頼らず--worktreeでセッション開始前にworktreeを確定させておくことの2つ。Linux/WSL2でworktreeを多用する運用をしているなら、修正が入るまではこのどちらかを既定にしておいたほうが安全だと思う。