Claude Media
Claude CodeがNixOSでbubblewrapに失敗する原因と対処

Claude CodeがNixOSでbubblewrapに失敗する原因と対処

NixOSのnix-direnv環境でClaude Codeのサンドボックス(bubblewrap、コマンド名bwrap)が失敗する原因と、設定でできる回避策です。

NixOSでnix-direnvを使うリポジトリでClaude Codeのサンドボックスを有効にすると、Bashコマンドがすべてbwrap: Can't create fileエラーで止まることがあります。原因は、.env系ファイルを守る読み取り拒否リストに、/nix/storeへのシンボリックリンクを経由するパスが混ざることです。GitHub issueではAnthropicのエンジニアが再現を確認しています。ただしissueは2026年10月22日にstaleラベル付きでclosed(not planned)になっており、修正の記載はありません。ここでは症状の見分け方、原因、設定でできる回避策を順に示します。

seccompの権限ビットだけが壊れている場合は、NixOSでsandboxがPermission deniedになる原因を確認してください。メッセージがexecvp /bin/bashのときはmerged-usrの問題です。

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)と、nix-direnvを使うNix flakeリポジトリの組み合わせです。

このissueは2026年8月15日に起票され、platform:linuxとarea:sandboxのラベルが付いています。reproducedラベルは8月25日の確認コメントの直後に付きました。duplicateラベルも付いており、10月22日にstaleラベル付きでclosed(not planned)になりました。

失敗までの流れは次の5段階です。

手順

エラーが出るまでの流れ

  1. 1

    nix-direnvがGCルートを作る

    flakeの入力を、.direnv/flake-inputs/<ハッシュ>-sourceというシンボリックリンクで保持します。リンク先は読み取り専用の/nix/storeです。

  2. 2

    リポジトリ自身もstoreにコピーされる

    flake自身のソース(self)も入力の1つなので、.env.exampleのような.env系ファイルをGit管理していると、storeのコピーにも含まれます。

  3. 3

    読み取り拒否リストにstore側のパスが載る

    作業ツリーの.env.exampleに加えて、.direnv配下のコピーも拒否対象になります。issueはこれを「自動の秘密情報保護」の走査結果と説明しています。

  4. 4

    bwrapがマスクを作ろうとして失敗する

    拒否対象のパスには、読めなくするマスクを重ねます。シンボリックリンクを経由した作成先に対して、bwrapがCan't create fileを返します。

  5. 5

    そのリポジトリの全Bashコマンドが止まる

    1エントリの失敗でサンドボックスの準備全体が落ちるため、コマンドの内容は関係なく実行前に失敗します。

なぜ/nix/storeを経由するとマスクに失敗するか

issueのコメントで、Anthropicのbcherny氏がbubblewrap 0.9.0を使い、報告と同じ構成を用意して再現を確認しています。構成は、Git管理された.env.exampleと、読み取り専用の/nix/storeコピーを指す.direnv/flake-inputs/<ハッシュ>-sourceのシンボリックリンクです。

確認で分かった要点は2つです。

  1. 失敗の引き金はシンボリックリンクになっている親ディレクトリで、読み取り専用の/nix/storeそのものではありません。書き込み可能なディレクトリへのシンボリックリンクを経由しても、同じ形でマスクは失敗します
  2. 解決(resolve)済みのstoreパスを直接マスクする場合は、読み取り専用の/nix/storeマウントの内側でも成功します

問題は「読み取り専用の場所を保護できない」ことではなく、リンクを解決しないまま、マスク対象のパスを組み立てていることにあります。issueが望ましい挙動として挙げているのは、リンクを解決してからマスクする、またはマスクできないエントリを警告付きでスキップする、のどちらかです。

自分の環境が同じ原因かを切り分ける

bwrap: Can't ...で始まるエラーは原因が複数あるため、エラー文のパスと状況で分けます。

  • パスに.direnv/flake-inputs/が入っていて、リポジトリが.env*ファイルをGit管理している場合は、このissueの症状です
  • Can't mount tmpfs on /newroot/home/<user>/.awsのようにmountで始まる場合は、自分でdenyReadに書いたパスがシンボリックリンクになっている可能性があります
  • メッセージの末尾がRead-only file systemの場合は、作成先が読み取り専用でマウントされている可能性があります。このissueのNo such file or directoryとは別の報告です。作業ディレクトリの親をallowReadに入れている場合(issue #103955)や、.claude/worktrees/配下のworktreeに入った後のセッション(issue #95949)で報告されています
  • Operation not permittedやPermission deniedが出る場合は、AppArmorなどのユーザー名前空間の制限が先に疑われます。Ubuntuでの確認手順はClaude Code Ubuntuインストールガイドにあります

2つ目の型は別のissue(#93845)で報告されています。WSL2で~/.awsをWindows側の/mnt/c/Users/<user>/.awsへのシンボリックリンクにし、denyReadにそのパスを入れると、lsのような単純なコマンドまで失敗します。存在しない拒否パスは黙ってスキップされ、リンクになっている1本だけが失敗する、という報告です。管理設定から届いた拒否ルールは、ユーザー側の設定で外せません。

同じissueのコメントには、WSL2以外でも起きるという報告があります。その報告では、Claude Code v2.1.283が同梱するサンドボックス部品の旧版で、denyReadのディレクトリをリンクのまま--tmpfsに渡すため、bubblewrap 0.12.0がCan't mount on symlink destinationで拒否するとされています。症状も原因もNixOSのissueとは別ですが、「読み取り拒否パスがシンボリックリンクだと、bwrapの準備全体が落ちる」という点は共通です。sops-nixの/run/secretsが原因でevalのBashが全滅する型は、claude plugin evalがsops-nix環境でBash全滅する原因で扱っています。

保護を外す回避策は2つある

設定でできる回避策は、保護を一部または全部外す方向になります。自動リストの生成方法を変える設定は、公式ドキュメントに載っていません。

くらべる

どこまで隔離を残すか

ネットワークは隔離したまま

filesystem.disabled

ファイルシステムの隔離だけを止めます。ネットワークの許可ドメインは効き続けます。

サンドボックスを使わない

sandbox.enabled: false

OSレベルの隔離をすべて手放し、Bashコマンドは従来の許可プロンプトで運用します。設定を書かなければ、これが既定の状態です。

ファイルシステム隔離だけを止める

ネットワーク隔離を残したまま、自動リストが原因の失敗を避けたい場合は、sandbox.filesystem.disabledが候補になります。ユーザー設定の~/.claude/settings.jsonに書きます。

{
  "sandbox": {
    "enabled": true,
    "filesystem": {
      "disabled": true
    }
  }
}

このキーはv2.1.216以降で使え、既定値はfalseです。ユーザー設定・管理設定・--settingsフラグでのみ有効で、プロジェクトの.claude/settings.jsonや.claude/settings.local.jsonには書けません。チェックアウトしただけのリポジトリが、ファイルシステム隔離を勝手に解除できないようにするためです。環境変数CLAUDE_CODE_SUBPROCESS_ENV_SCRUBを設定している場合は、どの設定元の値も無視され、隔離は有効なままです。管理設定がsandbox.filesystemを持つ場合や、管理設定のcredentials.filesにdenyのエントリがある場合は、このキーは管理設定からしか立てられません。

設定ファイルを書き換える前に、--settingsフラグで一回限りの起動として試せます。claude --helpには次のように載っています(v2.1.289で表示)。

  --settings <file-or-json>             Path to a settings JSON file or a JSON
                                        string to load additional settings from

JSON文字列をそのまま渡せるので、設定ファイルを触らずに次のように起動できます。

claude --settings '{"sandbox": {"enabled": true, "filesystem": {"disabled": true}}}'

これで同じリポジトリのBashコマンドが通るようになれば、原因はファイルシステム層にあると絞れます。通らなければ、別の原因です。

隔離をやめるとコマンドの書き込み範囲が広がるため、公式ドキュメントは次の点も挙げています。

  • autoAllowBashIfSandboxedの既定値はtrueのままなので、プロンプトを残したい場合はfalseにします
  • サンドボックス内のコマンドは、ユーザー単位の一時ディレクトリではなく、シェルの$TMPDIRを引き継ぎます
  • ファイルシステム隔離が切れた状態で自動許可されたコマンドは、シェルの起動ファイルや$PATH上の実行ファイルを書き換え、次回の実行で自分の権限を広げられます

最後の点は、信頼できるワークロードにだけ使うよう、公式が警告している内容です。一般的なdenyRead / allowReadの挙動はsandbox.filesystem.denyReadで認証情報ファイルを読ませないにまとまっています。

サンドボックスごと無効化する

filesystem.disabledを立ててもなお同じ形のエラーが出る構成では、切り分けを続けるより、一旦サンドボックスそのものを切るほうが早いこともあります。

{
  "sandbox": {
    "enabled": false
  }
}

/sandboxパネルのModeタブで選べるのは、自動許可と通常の許可プロンプトの2択です。サンドボックスを切る操作はこのタブにはなく、設定ファイルのsandbox.enabledで行います。

allowUnsandboxedCommandsが既定のtrueのままなら、Claudeが失敗したコマンドをサンドボックス外で再試行することがあります。その際は、権限モードに応じた承認が入ります。ただしこれはコマンド単位の一時退避で、原因は直りません。issueの報告者も「コマンドごとのバイパスは動くが、サンドボックスの意味を失わせる」と評しています。仕組みはallowUnsandboxedCommandsは抜け道を管理設定で塞ぐ設定で扱っています。

nix-direnv特有のつまずき

GCルートの削除は次の評価で元に戻る

.direnv/flake-inputs/<ハッシュ>-sourceのシンボリックリンクを手で消すと、サンドボックスは直ちに復帰します。ただし、nixは次の評価で同じGCルートを再生成します。ハッシュはflakeの入力内容に紐づくので、ソースを1文字変えれば新しいハッシュで作り直されます。issueの報告者も、削除しても次のセッションで再発すると書いています。削除は毎セッションの応急処置にしかなりません。

direnv reloadでは直らない

direnv reloadはこの問題を解決しません。/nix/store側のパスは最初から存在していて、消えていたわけではないからです。

条件はflakeのソースに追跡された.env系ファイルがあること

issueが挙げる影響範囲は、flakeのソースに追跡された.env*ファイルを含むnix-direnvのリポジトリです。再現に使われた構成も、.env.exampleを追跡した状態でした。

完全一致のdenyReadはより広いallowReadで開き直せない

今回の読み取り拒否エントリはユーザーが書いたものではありませんが、denyReadと同じ拒否として働いているようです。公式ドキュメントでは、完全一致またはワイルドカードのdenyReadエントリは、より広いallowReadの内側でも拒否が保たれます。allowReadで.env系だけを開き直す方法は、当てにできません。ディレクトリ単位のdenyReadなら、より狭いallowReadで一部を開けられます。

もう1点、サンドボックスの公式ページと、このissueの説明には食い違いがあります。ページの認証情報の節には「組み込みの認証情報拒否リストは無く、列挙したファイルと変数だけが制限される」とあります。一方issueの報告者は、.env.exampleが自動で拒否対象になったと説明しています。sandbox.credentials.filesを空にしても、この自動エントリが止まるかどうかは、公式ページからは読み取れません。

サンドボックス全体の権限モデルと、denyRead / allowReadの組み合わせ規則はClaude Codeのサンドボックス設計で扱っています。home-managerのsymlinkでsettings.jsonが消える別の症状は、settings.jsonが消える問題の記事にあります。

まとめ

v2.1.289までの公式changelogに、この症状を直したと読める項目は載っていません。守りたい認証情報がdenyRead頼みなら、filesystem.disabledで保護が外れることを先に確かめます。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. github.com/anthropics/claude-code/issues/86855
  2. github.com/anthropics/claude-code/issues/93845
  3. github.com/anthropics/claude-code/issues/103955
  4. github.com/anthropics/claude-code/issues/95949
  5. code.claude.com/docs/ja/sandboxing
  6. code.claude.com/docs/ja/settings-reference
  7. code.claude.com/docs/en/changelog
この記事を共有:XはてブLinkedIn