Claude Media
EXDEVでtmpfs環境のプラグインインストールが失敗する原因と対処

EXDEVでtmpfs環境のプラグインインストールが失敗する原因と対処

Claude Codeのプラグインインストールが、/tmpがtmpfsのときEXDEVエラーで失敗する原因とTMPDIR設定による回避策をまとめます。

/tmp がtmpfsマウントの環境で claude plugin install を実行すると、EXDEV: cross-device link not permitted でインストールが失敗することがあります。原因は、インストール処理が ~/.claude/plugins/cache と /tmp の間でディレクトリを移すとき、両者が別々のファイルシステムにまたがっていることです。TMPDIRを~/.claudeと同じファイルシステム上に向け直せば回避できます。

このエラーはGitHubのissue #14799として2025年12月20日に報告されました。報告時のバージョンは1.0.40で、/plugins の画面からプラグインを入れたときに出ています。エラーメッセージは次の形です。

Error: Failed to install: EXDEV: cross-device link not permitted,
rename '/home/user/.claude/plugins/cache/hiivmind-corpus-github' ->
'/tmp/claude-plugin-temp-1766187024245'

発生条件は2つです。/tmpがtmpfs(メモリ上に構築される一時ファイルシステム)としてマウントされていること、そして~/.claudeがext4やbtrfsなど別のファイルシステム上にあることです。tmpfsはUbuntu 25.10やFedora、Archなど多くのLinuxで/tmpの既定になっています。/homeだけ別パーティションに切り出している構成では、条件が揃いやすくなります。

claude plugin installのオプションはClaude Code plugin init/installコマンドの全オプションにまとめています。v2.1.289のclaude plugin install --helpに出るオプションは次の6つで、一時ファイルの置き場所を選ぶものはありません。

claude plugin install --help
オプション役割
-s, --scope役割user / project / local から選ぶ
--config役割プラグインのuserConfigを設定する
--json役割結果を1行のJSONで出す
-y, --yes役割マーケットプレイスが宣言したコマンドを確認なしで受け入れる
--accept-command役割前回の--jsonが示したsha256のコマンドだけを受け入れる
--registry役割<package>@npm形式のとき、取得元のnpmレジストリを替える

つまり、コマンドの引数ではEXDEVを避けられず、環境変数で逃がすことになります。

自分の環境が該当するかを判定する

回避策を試す前に、2つの条件を次のコマンドで見分けられます。

findmnt -T /tmp
findmnt -T "$HOME/.claude"

-T(--target)を付けると、任意のパスが属するファイルシステムが表示されます。/tmp側のFSTYPE列がtmpfsで、~/.claude側のSOURCE列が別デバイスを指していれば、条件に当てはまります。~/.claudeをまだ作っていないなら、$HOMEを指定します。findmntがない環境では、df ~/.claude /tmpでも、2つのパスが別デバイスかどうかを見分けられます。

ただし、条件が揃っても毎回失敗するわけではありません。失敗するかどうかは、次の節で述べるプラグイン側の名前で決まります。

原因: プラグイン名とマーケットプレイス名が同じだと遠回りの経路に入る

Node.jsのfs.rename()は、ファイルシステムの境界をまたぐ移動ができません。異なるマウントポイント間で呼ぶとEXDEVになります。issueのコメントには、インストーラーが2段階で動くという調査結果が書かれています。

手順

プラグインのインストールが通る経路

  1. 1

    ステージング

    プラグインのソースをcache/<plugin.jsonのname>にコピーします。

  2. 2

    ガードで分岐する

    確定先のパスが、ステージング先のパスの内側に入っていないかを調べます。

  3. 3

    確定

    入っていなければ、cache/<マーケットプレイス名>/<プラグイン名>/<バージョン>へ同じファイルシステム内で直接リネームします。入っていれば、os.tmpdir()(既定では/tmp)を経由する中間リネームが挟まり、/tmpがtmpfsならここでEXDEVです。

この調査は2026年2月9日付のコメントで、インストーラーの配布コードを読んだ第三者によるものです。公式に確認された仕様ではない点に注意してください。

内側に入る条件は、.claude-plugin/plugin.jsonのnameが配布元マーケットプレイスの名前と一致することです。

くらべる

名前が一致するか、しないか

EXDEVが起きうる

一致する

nameがmy-skillsのプラグインを、同名my-skillsのマーケットプレイスから配布する場合です。ステージング先cache/my-skillsが、確定先cache/my-skills/my-skills/1.0.0の先頭部分と重なります。

影響なし

一致しない

公式マーケットプレイスclaude-plugins-officialは、どのプラグイン名とも一致しません。そのため公式配布のプラグインは直接リネームの経路を通ります。

コメント欄の事例も、この見立てと矛盾しません。"source": "./.claude-plugin"のプラグインと、"source": "./"のプラグインで失敗が報告されました。一方で"source": "./"のプラグインが成功した例もあり、sourceの値そのものは決め手になっていません。

コメントは、Node.jsでEXDEVを扱う一般的なパターンとして、次のような修正案も示しています。

try {
  fs.renameSync(src, dest);
} catch (err) {
  if (err.code === "EXDEV") {
    fs.cpSync(src, dest, { recursive: true });
    fs.rmSync(src, { recursive: true, force: true });
  } else {
    throw err;
  }
}

issueは2026年2月13日にAnthropic側のコラボレーターが「次のバージョンで修正予定」とコメントし、同日クローズされました。公式のプラグイントラブルシューティングにもchangelogにもEXDEVに対応する記載はなく、修正版の番号は公表されていません。

今すぐ試せる回避策: TMPDIRを同じファイルシステムに向ける

issueで効いたと報告されている回避策は、TMPDIRを~/.claudeと同じファイルシステム上のディレクトリに向けることです。

mkdir -p ~/.claude/tmp
TMPDIR=~/.claude/tmp claude

毎回指定するのが煩わしい場合は、シェルの設定ファイルに書いておきます。

echo 'export TMPDIR="$HOME/.claude/tmp"' >> ~/.bashrc

TMPDIRを替えると、同じシェルから起動する他のプロセスの一時ファイルの置き場所も変わります。影響をclaudeだけに絞りたいときは、最初の例のように起動コマンドの前に付ける形が向いています。

CLAUDE_CODE_TMPDIRでは直らない可能性がある

Claude Codeには、専用のCLAUDE_CODE_TMPDIRもあります。公式ドキュメントの説明は「内部の一時ファイルに使うディレクトリを上書きする。Unixでは末尾に/claude-{uid}/が付く。Claude Code自身の一時ファイルは常にこの上書き先を使う」です。v2.1.5で追加された変数で、シェル、ユーザー設定、管理者設定のいずれかで指定できます。

ここで気をつけたいのは、EXDEVの中間リネームの行き先がos.tmpdir()だと報告されている点です。os.tmpdir()が何を見るかは、Node.js v20.18.0で次のように確かめられます。

node -e "console.log(require('os').tmpdir())"
# → /var/folders/.../T   (macOSの既定)
TMPDIR=/data/tmp node -e "console.log(require('os').tmpdir())"
# → /data/tmp
CLAUDE_CODE_TMPDIR=/x/y node -e "console.log(require('os').tmpdir())"
# → /var/folders/.../T   (変化なし)

os.tmpdir()が読むのはTMPDIR系の環境変数で、CLAUDE_CODE_TMPDIRは読みません。claudeの実行ファイルはNodeそのものではないので、この出力はインストーラーの挙動の証明にはなりません。それでもissueで効いたと報告されているのがTMPDIRだけである点と合わせると、まずTMPDIRで試す順序が堅実です。

CLAUDE_CODE_TMPDIRは、プロジェクトやローカルの.claude/settings.jsonのenvブロックに書いても効きません。設定リファレンスでは、CLAUDE_CODE_TMPDIR、TMPDIR、TMP、TEMPが「チェックアウトしたリポジトリに決めさせない変数」として、プロジェクト・ローカル設定では無視されると定められています。v2.1.251のchangelogにも、同じ趣旨の変更が載っています。

似た名前のCLAUDE_CODE_PLUGIN_CACHE_DIRは、別の役割の変数です。公式ドキュメントでは「プラグインのルートディレクトリを上書きする。名前に反して、キャッシュ自体ではなく親ディレクトリを指す」とされ、既定は~/.claude/pluginsです。一時ディレクトリではないので、EXDEVの回避には使えません。tmpfs上のパスを指定すると、インストール済みのプラグインがメモリ上に置かれ、再起動で消えるおそれもあります。

同じCLAUDE_CODE_TMPDIRでも、効く不具合と効かない不具合があります。tmpclaude-*-cwdファイルが消えない原因と削除方法の不具合では、この変数を設定しても対象ファイルの生成場所は変わらなかったと報告されています。

プラグイン作者の回避策: nameをマーケットプレイス名と変える

自分でマーケットプレイスを運用している場合は、.claude-plugin/plugin.jsonのnameを、配布先マーケットプレイスの名前と別の値にすれば、遠回りの経路に入りません。たとえばマーケットプレイスがmy-skillsなら、プラグインはmy-skills-coreのように変えます。

marketplace.jsonの各プラグインエントリでは、nameとsourceが必須です。issueのコメントでは、sourceを消して回避しようとするとInvalid schema: plugins.0.source: Invalid inputで読み込みに失敗すると報告されています。sourceには、git・npm・アーカイブなど複数の取得方法を指定できます。

影響が報告されている環境

issueのコメント欄に出てきた環境は次のとおりです。

環境/tmpの状況備考
Ubuntu 25.10/tmpの状況tmpfs備考最初の報告
Omarchy(Archベースのディストリビューション)/tmpの状況tmpfsが別マウント備考マーケットプレイス経由のインストールでも再現
macOS/tmpの状況/homeが別パーティション備考Claude CodeとCoworkの両方で再現との報告

issueの本文はUbuntu・Fedora・Archをtmpfsの/tmpが既定のディストリビューションとして例示しています。

EXDEVと紛らわしい別の症状を切り分ける

プラグインまわりには、EXDEVとは原因の違う症状もあります。エラー文や症状の違いで見分けられます。

  • ENOENTでリネームが失敗する: WSL2でカレントディレクトリがDrvFsパス(/mnt/d/など)のとき、報告されています。2つのパスは同じext4上にあり、EXDEVではありません。コメント投稿者はBunのrenameSyncの問題ではないかと推測しており、原因は未確定です。~/など、WSLのネイティブパスから実行すると成功したそうです
  • Could not move the new copy of this plugin version into <path>(ENOTEMPTY): 公式のトラブルシューティングに載っている別の症状です。インストール中に、ウイルススキャナーや別プロセスがバージョンのフォルダーを使っていると起きます。メッセージの文末で、以前のコピーが戻されたかどうかが分かります。The previously installed copy was moved backなら、以前のバージョンはそのまま使えます。再実行は、他のプログラムの処理が終わってからにします。v2.1.277では、使用中のバージョンを再インストールして失敗する問題が修正されています
  • /pluginの画面でインストールが失敗する: 詳細画面でEnterを押してインストールすると、失敗の全文エラーが表示されます。EXDEVかどうかは、この全文で見分けます
  • --scope projectで入れたプラグインが他のプロジェクトでも「インストール済み」に見える: Claude Codeのプラグインがグローバル誤検知される問題と対処法で扱っています
  • Windowsやネットワークファイルシステムでの一時ファイル残り: v2.1.210で、キャッシュ書き込みが失敗時に一時ファイルを残す問題と、ロック中ファイルのリネーム失敗が修正されています
  • プラグイン以外のインストールが止まる: ロックファイルの残留が原因になるケースをClaude Codeのロックファイル残留でインストール失敗する原因と直し方にまとめています

まとめ

公式マーケットプレイスのプラグインだけを使う人は、この問題を気にする必要がありません。コミュニティ製や自社製のプラグインでEXDEVが出たら、まずTMPDIRを~/.claudeと同じファイルシステムに向けます。自作のプラグインを配る側は、nameをマーケットプレイス名からずらすのが恒久策です。

参照した一次ソース

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

  1. github.com/anthropics/claude-code/issues/14799
  2. code.claude.com/docs/en/plugin-marketplaces
  3. code.claude.com/docs/en/env-vars
  4. code.claude.com/docs/en/plugins/troubleshooting
  5. code.claude.com/docs/en/settings-reference
この記事を共有:XはてブLinkedIn