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と同じファイルシステム上に向け直せば回避できます。
EXDEV: cross-device link not permittedエラーの正体
このエラーはGitHubのissue #14799として2025年12月20日に報告されました。実際のエラーメッセージは次のような形です。
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 21.04以降やFedora、Archなど多くのLinuxディストリビューションで/tmpの既定のマウント方式になっており、~/.claudeを含む/homeだけ別パーティションに切り出している構成では条件が揃いやすくなります。claude plugin installコマンド自体のオプションはClaude Code plugin init/installコマンドの全オプションにまとめていますが、--dry-runなどのオプションを付けても、このEXDEV自体は回避できません。
自分の環境が該当するかを確認する
回避策を試す前に、自分の環境が条件に当てはまるかを確認できます。
findmnt /tmp
findmnt "$HOME/.claude" 2>/dev/null || findmnt "$HOME"findmnt /tmpのTYPE列がtmpfsで、~/.claude側(または$HOME)のSOURCE列が別デバイスを指していれば、この記事の条件に当てはまります。findmntがない環境ではmount | grep -E "on (/tmp|$HOME) "でも同様に確認できます。
なぜ/tmpがtmpfsだと失敗するのか
Node.jsのfs.rename()は、ファイルシステムの境界をまたいだ移動に対応していません。異なるマウントポイント間でrename()を呼ぶと、このEXDEVエラーになります。プラグインのインストール処理は次の2段階で構成されています。
- ステージング: プラグインのソースを
cache/<plugin.jsonのname>にコピーする - 確定:
cache/<マーケットプレイス名>/<プラグイン名>/<バージョン>へリネームする
この2段階の間には、確定先のパスがステージング先のパスの内側に入り込んでいないかを調べるガード処理があります。GitHub上のコメントで報告された調査(コメント投稿者codemedic氏、2026年2月9日)によれば、確定先が内側に入り込んでいると判定された場合だけ、os.tmpdir()(既定では/tmp)を経由する中間リネームが挟まります。ここで/tmpがtmpfsだとEXDEVが起きます。逆に確定先がステージング先の内側に入り込んでいなければ、同じファイルシステム内で直接リネームされるため問題は起きません。
同コメントによれば、確定先が内側に入り込む条件は「.claude-plugin/plugin.jsonのnameフィールドが、そのプラグインを配布するマーケットプレイスの名前と一致すること」です。たとえばnameがmy-skillsのプラグインを、同じくmy-skillsという名前のマーケットプレイスから配布すると、ステージング先cache/my-skillsが確定先cache/my-skills/my-skills/1.0.0の先頭部分と一致し、中間リネームの経路に入ります。公式マーケットプレイスの名前(claude-plugins-official)はどのプラグイン名とも一致しないため、公式配布のプラグインはこの経路を通らず影響を受けません。
コメントは、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日に投稿者ashwin-ant氏が「次のバージョンで修正予定」とコメントした後、まもなくクローズされています。ただし公式の変更履歴には、このエラーメッセージに直接対応する行が見当たりません。修正が反映された具体的なバージョン番号は本記事では断定しません。
今すぐ試せる回避策 — TMPDIRを同じファイルシステムに向ける
issue内で確認されている回避策は、TMPDIRを~/.claudeと同じファイルシステム上のディレクトリに向けることです。
mkdir -p ~/.claude/tmp
TMPDIR=~/.claude/tmp claude毎回指定するのが煩わしい場合は、シェルの設定ファイルに永続化できます。
echo 'export TMPDIR="$HOME/.claude/tmp"' >> ~/.bashrcより的確な回避策 — CLAUDE_CODE_TMPDIRを使う
TMPDIRはOS全体の一時ディレクトリを切り替える汎用の環境変数のため、他のプロセスの挙動にも影響します。より的確なのは、Claude Code専用のCLAUDE_CODE_TMPDIRです。
公式ドキュメントは「Claude Code自身の一時ファイルは常にこの上書き先を使う」と明記しています。プラグインインストール時の中間ディレクトリもClaude Code自身が作る一時ファイルなので、この変数でEXDEVを回避できるはずです。指定したパスの下に、Unix系では/claude-{uid}/が自動的に追記されます。
export CLAUDE_CODE_TMPDIR="$HOME/.claude/tmp"この変数はシェル、ユーザー設定、管理者設定のいずれかに設定する必要があります。プロジェクトやローカルの.claude/settings.jsonのenvブロックには書けません。公式ドキュメントによれば、v2.1.251以降はチェックアウトしたリポジトリが制御すべきでない変数の1つとしてプロジェクト・ローカル設定から除外されており、書いても警告付きで無視されます。
似た名前のCLAUDE_CODE_PLUGIN_CACHE_DIRという変数もありますが、こちらはプラグインの保存先ルート(既定~/.claude/plugins)そのものを変更する変数で、インストール処理の一時ディレクトリとは別物です。この変数をtmpfs上のパスに向けると、インストール済みプラグイン自体がメモリ上に置かれることになり、再起動でプラグインが消えるおそれがあります。EXDEVの回避目的では使わないのが無難です。
同じCLAUDE_CODE_TMPDIRでも、効く不具合と効かない不具合があります。tmpclaude-*-cwdファイルが消えない原因と削除方法で扱った別の不具合では、この変数を設定しても対象ファイルの生成場所は変わらなかったと報告されています。今回のEXDEVとは発生箇所が異なるため、混同しないよう注意してください。
プラグイン作者ができる回避策 — マーケットプレイス名と揃えない
自分でマーケットプレイスを運用してプラグインを配布する場合は、.claude-plugin/plugin.jsonのnameフィールドを、配布先マーケットプレイスの名前と異なる値にすることで、この経路そのものを避けられます。公式ドキュメントでは、マーケットプレイスのmarketplace.jsonにリストする各プラグインエントリでnameとsourceの2つが必須フィールドと定義されています。sourceにはgit・npm・アーカイブなどいくつかの取得方法を選べますが、報告されているケースはいずれも相対パス形式("./" や "./.claude-plugin")を使ったものでした。
影響が確認されている環境
issueのコメント欄で確認されている環境は次のとおりです。
| 環境 | /tmpの状況 | 備考 |
|---|---|---|
| Ubuntu 25.10 | /tmpの状況tmpfsが既定 | 備考最初の報告環境 |
| Fedora / Arch Linux | /tmpの状況tmpfsが既定 | 備考同様のEXDEVを報告 |
| Omarchy(Archベースのディストリビューション) | /tmpの状況tmpfsが別マウント | 備考マーケットプレイス経由のインストールでも再現 |
| macOS | /tmpの状況/homeが別パーティション | 備考Claude CodeとCoworkの両方で再現との報告 |
これとは別に、WSL2でカレントディレクトリがDrvFsパス(/mnt/d/など)のときにプラグインインストールが失敗するという報告もコメント欄に寄せられています。ただしこちらはEXDEVではなくENOENTで失敗しており、コメント投稿者はBunのrenameSync実装に起因する別の問題ではないかと推測しています。原因は未確定なので、/tmpがtmpfsの場合とは切り分けて考える必要があります。
関連する既知のプラグイン不具合
プラグイン周りにはEXDEV以外にも、ファイルシステムやキャッシュの扱いに起因する既知の不具合が報告されています。--scope projectでインストールしたプラグインが別プロジェクトでも「インストール済み」と誤表示される問題はClaude Codeのプラグインがグローバル誤検知される問題と対処法で扱っています。プラグイン以外のインストール失敗では、ロックファイルの残留が原因になるケースをClaude Codeのロックファイル残留でインストール失敗する原因と直し方にまとめています。いずれも今回のEXDEVとは別の原因ですが、インストール系のトラブルシューティングとして併せて確認すると切り分けが早くなります。
よくあるつまずき
marketplace.jsonからsourceフィールドを削除して回避しようとする:sourceは必須フィールドのため、削除するとInvalid inputという別のエラーになります。EXDEVの回避には使えません- 公式プラグインでも起きると思い込む: 公式マーケットプレイス(
claude-plugins-official)の名前は、配布するどのプラグイン名とも一致しないため、この経路を通りません。影響は主にコミュニティ製・自社製のプラグインです CLAUDE_CODE_PLUGIN_CACHE_DIRをtmpfs上に向けて解決しようとする: この変数はプラグインの永続的な保存先を変えるものです。tmpfs上に向けるとインストール済みプラグインが再起動で消える可能性があります
まとめ
/tmpがtmpfsで~/.claudeが別ファイルシステムにある環境では、プラグインのインストールがEXDEV: cross-device link not permittedで失敗することがあります。原因は、確定先のパスがステージング先のパスの内側に入り込む特定の条件下で、Claude Codeが/tmp経由の中間リネームを挟む処理にあります。当面の回避策は、TMPDIRか、より対象が絞られたCLAUDE_CODE_TMPDIRを~/.claudeと同じファイルシステム上のパスに設定することです。自分でマーケットプレイスを運用する場合は、プラグイン名をマーケットプレイス名と重複させないことでも回避できます。