Claude CodeがNixOS環境でbwrapエラーになる原因と対処法
NixOSのnix-direnv環境でClaude Codeのbwrapサンドボックスが失敗する原因と、設定でできる回避策です。
NixOSでnix-direnvを使うリポジトリでClaude Codeのサンドボックスを有効にすると、Bashコマンドがすべてbwrap: Can't create fileエラーで止まることがあります。原因は、Git管理された.env系ファイルを自動的に保護するリストに、/nix/storeのシンボリックリンクを経由するパスが混ざることです。GitHub issueでAnthropicのエンジニアが再現を確認していますが、恒久的な修正はまだ入っていません。公式ドキュメントで裏付けが取れる回避策を、優先順位つきで示します。
NixOSのnix-direnv環境で起きるbwrapエラー
sandbox.enabledをtrueにしたNixOSのリポジトリで、Claude Codeがどんな単純なBashコマンドを投げても実行前に失敗します。bwrapはbubblewrapの実行コマンドで、Linux上で非特権のサンドボックス隔離をつくるツールです。エラーメッセージは次の形です。
bwrap: Can't create file at /home/<user>/dev/<repo>/.direnv/flake-inputs/<hash>-source/.env.example: No such file or directoryファイル自体はホスト上に実在します。にもかかわらずbwrapは「作成できない」と報告し、サンドボックス化されたコマンドが1つも通らなくなります。報告された環境は次の組み合わせです。
- Claude Code v2.1.228、Linuxネイティブサンドボックス(bwrap)
- NixOS、nix-direnvを使うNix flakeリポジトリ
このissueは2026年8月15日に起票され、platform:linuxとarea:sandboxのラベルが付いています。Anthropicのエンジニアが確認コメントを残した後も状態はopenのままで、修正コミットに紐づくクローズはまだ行われていません。
nix-direnvは、flakeの入力(flake input)をGCルート(ガベージコレクションの対象から外す参照)として.direnv/flake-inputs/<ハッシュ>-sourceにシンボリックリンクで保持します。リンク先は読み取り専用の/nix/store配下のコピーです。リポジトリが.env.exampleのような.env系ファイルをGit管理していると、そのファイルは/nix/store側のコピーにも含まれます。Claude Codeの自動保護は、作業ツリー本体の.env.exampleだけでなく、この.direnv配下のコピーにも読み取り拒否のエントリを追加します。シンボリックリンクを経由したこのパスが、後述する理由でbwrapのマスク処理と相性が悪く、サンドボックス全体が起動の時点で落ちます。
bubblewrapそのものの役割や/sandboxパネルからの有効化手順はClaude Code Ubuntuインストールガイドにまとまっています。この記事ではNixOS固有の症状に絞って扱います。
なぜ/nix/storeを経由するとマスクに失敗するか
Claude Codeのサンドボックスは、保護対象のファイルを読ませないため、bubblewrapに「そのパスへマスク(空の読み取り不能ファイル)を作れ」と指示します。GitHub issueのコメントで、Anthropicのbcherny氏がbubblewrap 0.9.0を使い、報告と同じ構成(トラッキングされた.env.exampleと、読み取り専用の/nix/storeコピーを指す.direnv/flake-inputs/<ハッシュ>-sourceシンボリックリンク)を用意して再現を確認しています。
確認で分かった要点は3つです。
- 失敗の引き金はシンボリックリンクになっている親ディレクトリであって、読み取り専用の
/nix/storeそのものではありません。書き込み可能なディレクトリへのシンボリックリンクを経由しても、同じ形でマスクは失敗します - 完全に解決(resolve)済みのstoreパスを直接マスクする場合は、読み取り専用の
/nix/storeマウント内であっても成功します - 自動保護リストが
.direnv/flake-inputs/<ハッシュ>-source/.env.exampleというシンボリックリンク経由のパスを拾った時点で、そのコマンド以降すべてのサンドボックスセットアップが失敗します
つまり問題は「読み取り専用の場所を保護できない」ことではなく、シンボリックリンクを解決せずにマスク対象のパスを組み立てていることにあります。issueのExpectedセクションでは、シンボリックリンクを解決してからマスクする、またはマスクできないエントリを警告付きでスキップする対応が提案されています。
回避策の早見表
根本原因である自動保護リストの生成方法自体は、ユーザー側の設定で直接編集できません。現実的な選択肢は、保護の仕組みを部分的または全体的に迂回することになります。
| 対処法 | 効果 | トレードオフ |
|---|---|---|
| 問題のシンボリックリンクを都度削除 | 効果即座にサンドボックスが復帰する | トレードオフnixが次の評価で同じGCルートを再生成し、再発する |
sandbox.filesystem.disabled: true | 効果ファイルシステム隔離だけを止め、ネットワーク隔離は維持する | トレードオフdenyReadやcredentials.filesによるファイル保護が丸ごと効かなくなる |
sandbox.enabled: false | 効果サンドボックスを使わない従来の許可プロンプト運用に戻す | トレードオフOSレベルの隔離そのものを失う |
| 個別コマンドを未サンドボックスで再試行 | 効果そのコマンドだけは動く | トレードオフコマンド単位の一時退避で、毎回の通常プロンプト管理に戻る |
上から「隔離をどこまで維持したまま迂回できるか」の順に並べています。
サンドボックスを維持したままファイルシステム隔離だけ止める
ネットワーク隔離のメリットは失いたくないが、自動保護リストが原因でBashコマンドが1つも通らない場合は、sandbox.filesystem.disabledが候補になります。ユーザー設定または管理設定のsettings.jsonに次のように書きます。
{
"sandbox": {
"enabled": true,
"filesystem": {
"disabled": true
}
}
}このキーはユーザー設定・管理設定・--settingsフラグでのみ有効です。プロジェクト設定の.claude/settings.jsonや.claude/settings.local.jsonには書けません。チェックアウトしただけのリポジトリが、ファイルシステム隔離を勝手に解除できないようにするためです。
隔離したいのはネットワークだけ、というケースに向いた設定です。一般的なdenyRead / allowReadの挙動そのものはsandbox.filesystem.denyReadで認証情報ファイルを読ませないにまとまっています。
それでも詰まる場合はサンドボックスごと無効化する
filesystem.disabledを立ててもなお同じ形のエラーが出る構成に当たった場合、切り分けを続けるより、一旦サンドボックスそのものを切って作業を続けるほうが早いこともあります。
{
"sandbox": {
"enabled": false
}
}sandbox.enabledは既定でfalseです。値を書かなければサンドボックスは動かず、Bashコマンドは従来どおり許可プロンプトを経由して実行されます。/sandboxパネルのModeタブから一時的に切り替える方法も使えます。
allowUnsandboxedCommandsが既定のtrueのままなら、失敗したコマンドをサンドボックス外で再試行する選択肢も出ます。ただしこれはコマンド単位の一時退避で、原因を直すものではありません。詳しい仕組みはallowUnsandboxedCommandsは抜け道を管理設定で塞ぐ設定で扱っています。issueの報告者も「コマンドごとのバイパスは動くが、サンドボックスの意味を失わせる」と評しています。
nix-direnv特有のつまずき
GCルートはソースが変わるたびハッシュが変わる
.direnv/flake-inputs/<ハッシュ>-sourceのハッシュは、flakeの入力内容に紐づきます。シンボリックリンクを手で消して一時的に直しても、ソースを1文字変えてdirenv reloadが走れば、nixは新しいハッシュで同じ構造のGCルートを再生成します。削除は毎セッションの応急処置にしかなりません。
direnv reloadでは直らない
報告者も試していますが、direnv reloadはこの問題を解決しません。/nix/store側のパス自体は最初から存在しており消えていたわけではないので、direnvの再読み込みでは状態が変わらないからです。
対象はトラッキングされた.env系ファイルを持つflakeリポジトリ全般
この症状が起きるのは、リポジトリのflake.nix自身がNixの評価対象(selfソース)としてstoreにコピーされ、かつそのリポジトリが.env.exampleのような.env系ファイルをGit管理している場合です。.envをGit管理しない、あるいは.gitignoreで除外している場合は、そもそも保護対象に含まれないため問題自体が起きません。
denyReadの通常運用との違い
ここまでの回避策は、いずれも保護の仕組み自体を弱める方向の対処です。通常時のsandbox.filesystem.denyReadは、ユーザーが明示的にリストしたパスだけを読み取り拒否する設定で、狭い範囲のallowReadで個別に再開放できます。今回の自動保護エントリはユーザーが直接書いたものではないため、allowReadで狙い撃ちして再開放できるかどうかは公式ドキュメントで明言されていません。確実に効くと分かっている対処は、この記事で挙げたfilesystem.disabledかsandbox.enabled: falseのように、保護の範囲をまとめて外す方法に留まります。
混同しやすい設定にsandbox.credentials.filesがあります。こちらは認証情報ファイルをdenyまたはmaskで守るための設定ですが、公式ドキュメントは「組み込みの認証情報拒否リストは無く、リストしたファイルと変数だけが保護される」と明記しています。つまり今回の自動保護はsandbox.credentials.filesの設定を書いていなくても働く、別系統の仕組みです。sandbox.credentials.filesを空のままにしても、この.env保護そのものは止まりません。
サンドボックス全体の権限モデルと、denyRead / allowReadの組み合わせルールはClaude Codeのサンドボックス設計で扱っています。
まとめ
NixOSのnix-direnv環境でClaude Codeのbwrapサンドボックスが全滅するのは、Git管理された.env系ファイルが/nix/store側にもコピーされ、そのシンボリックリンク経由のパスがClaude Codeの自動保護リストに入るためです。Anthropicのエンジニアが同じ構成で再現を確認し、シンボリックリンクを解決してからマスクすれば成功することも分かっていますが、v2.1.274までの公式changelogに該当する修正コミットは見当たりません。当面はネットワーク隔離だけ残すsandbox.filesystem.disabledか、サンドボックスそのものを切るsandbox.enabled: falseのどちらかで運用を続けることになります。シンボリックリンクを都度消す方法は同じセッション内でも再発するため、恒久策にはなりません。