Claude Media
Claude Codeのsettings.jsonが消える — home-managerのsymlink運用の対処

Claude Codeのsettings.jsonが消える — home-managerのsymlink運用の対処

Nixのhome-managerでsettings.jsonをsymlink管理していると、バックグラウンドワーカーがsymlinkを削除する報告があります。症状の見分け方、原因の確かめ方、回避策をまとめます。

Nixのhome-managerで~/.claude/settings.jsonを管理していて、いつの間にか権限ルールやhooksが効かなくなっているなら、ファイルそのものが消えている可能性があります。anthropics/claude-codeのissue #88307は、デーモン配下のバックグラウンドワーカー(claude bg-spare)が、このsymlinkをunlinkatで削除したと報告しています。

報告者はhome-manager switchのたびに、数秒から約1分でsymlinkが消える状況を、カーネルの監査ログで確認しています。同じ場所に通常のファイルを置くと、消えなくなったそうです。

何が消えるのか

消えるのは設定の中身ではなく、~/.claude/settings.jsonというパスそのものです。報告によると、消えた後は全セッションが組み込みの既定値に戻り、エラーはUIのどこにも出ません。

影響は次のとおり、設定ファイルに書いていたものすべてに及びます。

  • 権限ルール(permissions)
  • hooks
  • サンドボックス設定
  • modelとenv
  • ステータスライン

「エージェントの挙動がなぜか変わった」という以外に手がかりがありません。設定が飛んだことに気づくまでが長引く点が、この不具合のいちばん厄介なところです。

症状の要点

  • 対象はsettings.jsonがsymlinkで、その行き先が読み取り専用ディレクトリにある構成
  • 削除するのはデーモン配下の長寿命ワーカーで、端末には紐づかない
  • 通常ファイルのコピーに置き換えると、報告者の環境では止まった

発生する条件

issueの報告者は、次の条件が重なったときに起きたと書いています。

条件内容
settings.jsonの形内容symlink
symlinkの行き先内容読み取り専用のディレクトリ(Nixでは/nix/store)
削除の主体内容claude bg-spareで動く、デーモン配下のバックグラウンドワーカー
ワーカーの状態内容2日間アイドルのまま生きていた個体
きっかけ内容home-manager switchなどでsymlinkが作り直される

Nixとhome-managerでは、home-manager switchが~/.claude/settings.jsonを/nix/store配下へのsymlinkとして作るのが標準の動作です。報告者はこれを理由に、特殊な構成ではないと指摘しています。

一方で、再現しなかった組み合わせも明記されています。

  • 空のHOMEで新しく起動した--bg-spareワーカーは、120秒の間symlinkに触れなかった
  • claude plugin install、claude plugin marketplace add、claude doctor、claude mcp add --scope user、claude -pは、書き込みでEROFSを出したがsymlinkは残った

つまり、symlinkを張り替えただけでは足りず、実際にデーモンへ接続していて、しばらく生きているワーカーが必要なようです。

自分の環境で起きているか確かめる

まず、パスの状態を見ます。

ls -la ~/.claude/settings.json
pgrep -af 'bg-spare'

lsが「No such file」を返し、pgrepにclaude bg-spare ...が出ていれば、条件に合っています。pgrepの出力にあるワーカーが長く生きているほど、報告と状況が近くなります。

「誰が消したか」を確定するには、報告者と同じくカーネルの監査ルールを使います。issueの手順では、次のコマンドでsettings.jsonへの書き込み・属性変更を監視します。

sudo auditctl -w "$HOME/.claude/settings.json" -p wa -k claude-settings
sudo ausearch -k claude-settings -i | tail -40

出力で見るのは、次の3点です。

  • syscall=unlinkat(削除の呼び出し)
  • exeがClaude Codeのバイナリ(Nixでは.claude-wrapped)
  • nametype=DELETEのPATHレコードでmode=0120777(symlinkそのものを消した印)

報告者の記録では、proctitleがclaude bg-spare --bg-spare /tmp/cc-daemon-1000/.../spare/....claim.sockで、tty=(none)でした。端末に紐づかない点が、手元で操作して消した場合との見分けになります。

なぜ消えるのか — 報告者の推測と未確認の点

報告者が証明したのは、同じpidのワーカーがunlinkatでsymlinkを4回消したことまでです。削除の前に書き込みの失敗があったのかどうかは、確認できていません。

原因として挙げられているのは、関連する2つのissueとの組み合わせです。

  • #78162 — 設定の保存は一時ファイル(settings.json.tmp.<pid>)を書いてrenameする方式で、symlinkは1段だけ解決される。行き先が読み取り専用のディレクトリだとEROFSかEACCESで失敗する
  • #86935 — 設定ファイルは親ディレクトリごと監視されている。Nixでは親が/nix/storeになるため、home-manager側の入れ替えがワーカーを起こす引き金になりうる

報告者はバイナリの文字列から、書き込み処理に「symlinkを通した書き込みは拒否する」「インプレースのフォールバックで書いた」といったメッセージがあるのを見ています。O_NOFOLLOWではsymlinkを開けないので、フォールバックが通常ファイルを置くために先にsymlinkを消す、という筋道を推測しています。ただし制御フローは確かめておらず、順序は未証明と明記されています。

落とし穴

読み取り専用の行き先が、書き込みの失敗と削除の両方を呼ぶ

保存に失敗するだけなら、設定は古いままで済みます。報告されているのは、その先でsettings.json自体が消えるケースです。保存の失敗を放置すると、サイレントな設定の喪失に変わる可能性があります。

回避策 — 通常ファイルにする

報告者の環境では、symlinkを通常ファイルのコピーに置き換えた直後から削除が止まりました。コピー後は一度も触られていません。

cp --remove-destination "$(readlink -f ~/.claude/settings.json)" ~/.claude/settings.json

このコマンドはsymlinkが残っているうちに使います。既に消えている場合は、.nix側の定義などからsettings.jsonを作り直して通常ファイルとして置きます。

公式のsettingsページにも、同じ方向の記述があります。保存先の~/.claude/settings.jsonに書き込めない場合、たとえば別のツールが生成している、あるいは読み取り専用のコピーにリンクしている場合は、変更が現在のセッションだけに適用されて次のセッションで消えます。対処として、ファイルを生成するツール側でキーを設定するか、書き込めるファイルに置き換えるよう案内されています。

宣言的に管理したい人は、通常ファイルへのコピーをhome-manager switchの最後に行う、という形が考えられます。

# 例: activation で settings.json をコピーする形(動作は未検証。自環境の設定に合わせて調整)
home.activation.claudeSettings = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
  install -m 0644 ${./claude-settings.json} "$HOME/.claude/settings.json"
'';

このスニペットはissueに書かれた手順ではなく、「通常ファイルを置く」という報告者の回避策をhome-managerで再現するための一例です。Claude Code側が通常ファイルを書き換えた場合、次回のhome-manager switchで上書きされる点には注意が必要です。

symlinkを二重にするだけでは直らない

#78162は、symlinkの先がさらにsymlinkになっている構成(home-managerのmkOutOfStoreSymlinkなど)でも、EROFSで失敗すると報告しています。書き込み先が最終的には書き込み可能でも、1段目の行き先が/nix/storeだと一時ファイルがそこに作られるためです。書き込み可能な場所へ向ければよい、という単純な話ではありません。

ワーカーを止めれば止まるのか

報告された削除は、すべて長寿命の同一ワーカーが行っていました。ただし、ワーカーやデーモンを止めれば確実に防げるのかは、issueに記載がありません。claude daemon stopというコマンド自体はchangelogに登場しますが、この症状への効果は確かめられていません。

修正は入ったのか

issue #88307は、起票した報告者が2.1.219で確認し、2.1.219から2.1.237までのchangelog(380項目、18リリース)に該当の修正が無いことを確かめて書かれています。issueのコメントには、2.1.247のchangelogで修正されたようだという指摘が1件あります。

その2.1.247には、次の項目があります。

Fixed the Bash sandbox's after-command cleanup deleting a dotfile-managed ~/.claude/settings.json symlink (nix/home-manager, stow) when it is repointed outside the sandbox's writable area

これはBashサンドボックスのコマンド後クリーンアップが対象で、症状もnix/home-managerのsymlinkです。ただし、issueで特定されたのはデーモン配下のbg-spareワーカーで、サンドボックスのクリーンアップとは別の経路に見えます。issueはopenのままで、ワーカー経路の修正を示す項目は、changelogに載っていません。

手元のバージョンはclaude --versionで確かめられます。

サンドボックスを使う構成でsymlinkが消えたときは、2.1.247以降へ更新すると直る可能性があります。デーモン配下のワーカーが犯人だと監査ログで確かめた場合は、更新だけでは足りないかもしれません。

2.1.290の項目に、symlinkの設定ファイルの行き先への編集が、設定ファイルの権限確認なしに通っていた問題の修正もあります。こちらは削除ではなく権限の話です。

どちらの経路かを見分ける材料は、削除したプロセスです。監査ログのproctitleがbg-spareならデーモン配下のワーカー、端末に紐づいたセッションの子プロセスならサンドボックス側を疑います。

ほかの「Nixで動かない」症状との切り分け

NixOSやnix-direnvの環境では、似た時期に別の問題も報告されています。

症状原因の系統参照
サンドボックスが起動直後に失敗する原因の系統bwrapが読み取り拒否のエントリを扱えない参照NixOSのbwrapエラー
コマンドが権限エラーで止まる原因の系統サンドボックスの書き込み許可参照NixOSのサンドボックス権限エラー
設定ファイルが無くなる原因の系統symlinkの削除(本記事)参照—
更新後にバックグラウンドセッションが落ちる原因の系統デーモンとバイナリの食い違い参照Homebrewアップグレード後の障害

settings.jsonに何を書いていたかを復元するには、全項目の意味を把握しておくと楽です。settings.jsonの全設定項目の一覧で、消えた項目を見直せます。

まとめ

報告された条件は、symlinkの行き先が読み取り専用で、デーモン配下のワーカーが長く生きていることです。

回避策は、通常ファイルを置くことです。宣言的管理を諦めたくないなら、activationでコピーする形になりますが、これはissueの手順ではなく一例です。修正は、サンドボックスのクリーンアップ経路が2.1.247で入りました。ワーカー経路は、issueがopenのままです。

この記事を共有:XはてブLinkedIn