Claude Media
Plugin not loadedの原因と直し方 — Claude Codeのmanifest・ELOOP・EACCES

Plugin not loadedの原因と直し方 — Claude Codeのmanifest・ELOOP・EACCES

Plugin not loaded: its manifest was not readとskills path could not be checked (ELOOP)の違いを整理し、symlinkと権限の原因別に直し方を示します。

/pluginのErrorsタブにPlugin not loaded: its manifest ... was not readと出ている場合、そのプラグインからは何も読み込まれていません。スキルもフックもMCPサーバーも、すべて欠けた状態です。同じ画面にskills path could not be checked: ... (ELOOP)が出ているなら、プラグイン本体は動いていて、skills/だけが落ちています。

2つのエラーは見た目が似ていますが、被害の範囲も直し方も違います。共通する原因はsymlink(シンボリックリンク)とディレクトリの権限です。

2つのエラーは何を落とすのか

メッセージの文言と、読み込みが止まる範囲を並べると次のとおりです。

メッセージ止まる範囲主な原因
Plugin not loaded: its manifest <path> was not read止まる範囲そのプラグインの全部主な原因.claude-pluginかplugin.jsonがプラグイン外を指すsymlink、または権限不足
skills path could not be checked: <path> (ELOOP)止まる範囲skills/など該当の1パスのみ主な原因symlinkの循環、壊れたネットワークマウント、権限不足
<component> path escapes plugin directory止まる範囲該当の1パスのみ主な原因../で外を指す、またはプラグイン外へ抜けるsymlink

1行目は、マーケットプレイスのプラグインの.claude-plugin/plugin.jsonが、プラグインの内側にあると確認できなかったときに出ます。ローカルのマーケットプレイスにあるプラグインでは、マーケットプレイスの内側にあることも確認されます。確認できないと、そのプラグインからは何も読み込まれません。

2行目は、OSに「このパスはあるか」と問い合わせて、「見つからない」以外のエラーが返ったときのエラーです。skills/フォルダ、monitors/monitors.json、プラグインルートのSKILL.mdのような既定の置き場所が失敗しても、他のコンポーネントは読み込まれます。ただしプラグイン自身のディレクトリが失敗すると、プラグインごと読み込まれません。

存在しないパスではこのエラーは出ません。「ファイルが無い」と「確認できなかった」は別扱いです。

manifestが読まれない場合の直し方

エラー文の末尾には理由が付きます。例えば、ローカルマーケットプレイスの.claude-pluginフォルダが外部のdotfilesを指すsymlinkだと、次の形になります。

Plugin not loaded: its manifest /home/user/my-marketplace/my-plugin/.claude-plugin/plugin.json was not read, because it resolves to /home/user/dotfiles/my-plugin/.claude-plugin/plugin.json, outside the plugin directory and its marketplace directory

because it resolves toの後ろに実体の場所が書かれていれば、.claude-pluginフォルダかplugin.json自体がそこへ向くリンクです。dotfilesの管理ツールで設定をリンクにしている環境に当てはまる形です。

手順

manifestエラーの切り分け順

  1. 1

    理由の末尾を読む

    EACCESなどのエラーコードで終わっているか、resolves toで実体の場所が書かれているかを見ます。

  2. 2

    自分が作者かを決める

    入れただけのプラグインなら、メッセージを作者に報告します。自分で保守しているなら次へ進みます。

  3. 3

    リンクを実体に置き換える

    .claude-pluginフォルダかplugin.jsonがリンクなら、リンクを削除して指していた内容のコピーに置き換えます。

  4. 4

    再読み込みする

    /reload-pluginsを実行するか、Claude Codeを再起動します。

置き換えの操作は、手元のシェルで次のように行えます。my-pluginは読み替えてください。

# .claude-plugin がリンクかどうか確認する
ls -ld my-plugin/.claude-plugin
 
# リンクなら、指し先の内容で実体に置き換える
cp -RL my-plugin/.claude-plugin my-plugin/.claude-plugin.copy
rm my-plugin/.claude-plugin
mv my-plugin/.claude-plugin.copy my-plugin/.claude-plugin

cp -RLはリンクをたどって中身をコピーします。元のdotfilesと二重管理になるので、以後の編集はプラグイン側で行うか、プラグインをdotfilesから切り離す運用に変える必要があります。

「ELOOP」は循環、「EACCES」は権限

path could not be checkedのかっこ内は、OSが返したエラーコードです。コードごとの原因は次のとおりです。

  • ELOOP: パス上のsymlinkが自分自身を指しているか、循環している
  • EIOまたはESTALE: パスのあるネットワークマウントが壊れているか、古くなっている
  • EACCES: パスより上のディレクトリを辿る権限(実行権限)がない

claude plugin listでは同じエラーがPath not found: /home/user/my-plugin/skills (skills, ELOOP)の形で表示されます。文言が違うだけで、同じ原因です。

ELOOPをMacで再現する

ELOOPは、自分自身を指すリンクを作るだけで再現できます。次のとおり空のディレクトリで試し、Node.jsのstatSyncでエラーコードを見ると確かめられます(macOS、Node.js v20.18.0)。

ln -s skills skills
ls skills/
node -e "try{require('fs').statSync('skills')}catch(e){console.log(e.code)}"

lsはToo many levels of symbolic linksと答え、Node.jsはELOOPを返しました。ls -lで見るとskills -> skillsと、自分自身が指し先になっています。プラグインのskills/が同じ状態なら、エラーメッセージに出るコードもELOOPです。

自分を指すリンクを削除して実フォルダを作れば直ります。フォルダを作り直す前に、リンクの指し先をreadlinkで確かめると、意図した共有だったのか、作成ミスだったのかが分かります。

EACCESは「ファイル」ではなく「途中のディレクトリ」を見る

EACCESで見るのは、対象ファイルの読み取り権限ではありません。対象に至るまでの各ディレクトリの実行権限です。symlinkを経由している場合は、リンクを辿った先のディレクトリ列も確認する必要があります。

権限の穴はnamei -lで見つけられます(Linux)。macOSではnameiが使えない環境があるので、ls -ldを上位ディレクトリへ順にたどります。

namei -l /home/user/my-plugin/.claude-plugin/plugin.json

どこかに実行ビット(x)のないディレクトリがあれば、そこが原因です。chmod u+xで戻します。NASなど別マシンのマウントを経由しているなら、マウント先の権限も見ます。

path escapesとの違いと、symlinkの許される範囲

3つ目のpath escapes plugin directoryは、似たsymlink問題でも、コンポーネント単位で出ます。plugin.jsonやマーケットプレイスのエントリで宣言したパスが、プラグインの外に解決されると出て、その1パスだけが捨てられます。

拒否される対象は3つです。

  • ../shared-utilsのように、書き方の時点で外を指すパス
  • プラグイン外へ出るsymlink(同一マーケットプレイス内のプラグイン間リンクは除く)
  • macOSとLinuxで、パスのどこかにバックスラッシュを含むもの

3つ目は、プラグインの内側に収まるパスでも拒否されます。Windows区切りで書かれたプラグインはWindowsでは読み込まれ、他のOSでは拒否される理由がこれです。./commands/deploy.mdのようにスラッシュで書きます。

プラグイン間でファイルを共有したい場合は、プラグインのディレクトリ内にsymlinkを作る方法が用意されています。キャッシュにコピーされるときの扱いは、リンクの解決先で変わります。

リンクの解決先キャッシュでの扱い
プラグイン自身のディレクトリ内キャッシュでの扱い相対symlinkのまま残る
同じマーケットプレイス内の別の場所キャッシュでの扱い実体としてコピーされる
マーケットプレイスの外キャッシュでの扱いセキュリティのためスキップされる

ローカルのパスからインストールしたプラグインや、commandソースのcopyモードでは、プラグイン自身のディレクトリ内に収まるリンクだけが保たれ、残りはスキップされます。

つまり、「外部のdotfilesにあるスキルをリンクで取り込む」構成は、マーケットプレイスから入れるプラグインでは通りません。.claude/rulesにリンクを置く場合の挙動は別のsymlink問題で、プラグインとは仕組みが異なります。

ローカルマーケットプレイスで起きやすい理由

マーケットプレイスのプラグインは、入れ方によって読み込み方が変わります。ローカルのパスから追加したマーケットプレイスの相対パスのプラグインは、キャッシュにコピーされず、マーケットプレイスのフォルダ内からその場で読み込まれます。ソースを編集すると、次のセッション開始か/reload-pluginsで反映され、バージョンを上げる必要もありません。

手元のdotfilesやモノレポを、そのままローカルマーケットプレイスとして使う構成では、手元のリンクがそのまま検査の対象になります。manifest was not readのメッセージに「マーケットプレイスのディレクトリの外」という文言が入るのは、この場合に外側の境界も確かめられるためです。

それ以外のマーケットプレイスのプラグインはキャッシュにコピーされ、その時点でリンクの扱いが前掲の表のルールで決まります。

マーケットプレイスのエントリ自体が拒否される場合

プラグインのエントリ(marketplace.jsonのsource)が、マーケットプレイスの内側に解決できない場合も、別のエラーでプラグインが入りません。claude plugin installはits marketplace entry path does not stay inside the marketplace directoryと答え、すでに入っているプラグインはclaude plugin listでfailed to loadとなり、Plugin source path refusedと表示されます。

直し方は、sourceを./plugins/my-pluginのような、スラッシュ区切りの素直な相対パスで書くことです。途中で通るリンクも、マーケットプレイスの内側を指すようにします。URLで直接追加したマーケットプレイスでは相対パスのエントリが使えないため、gitリポジトリから追加し直すか、別のソース形式を使います。

バージョンによる違い

古いバージョンでは、同じ状況でもエラーが出ませんでした。

  • v2.1.265より前: 既定のコンポーネントフォルダを確認できないと、存在しないものとして扱い、エラーなしでそのコンポーネントを抜いたままプラグインを読み込んでいた
  • v2.1.257より前: プラグインが宣言したパスの検査は、パスの綴りだけを見ており、symlinkの行き先を見ていなかった
  • v2.1.251より前: マーケットプレイスエントリのcommandsパスは、プラグインの外を指していても読み込まれていた

古いバージョンで「エラーは出ないのにスキルだけ無い」場合は、同じ原因が黙って起きていた可能性があります。更新後に初めてエラーが現れるのは、検出が厳しくなったためです。

直したあとの確認

修正後は/reload-pluginsで再読み込みします。コマンドの動きは/reload-pluginsの解説にまとめています。ただしMCPの構成が変わる場合はキャッシュ無効化の警告が出ます。

CIや非対話実行では、読み込み失敗が出力に出ても終了コードは0のままになりがちです。plugin_errorsを失敗条件にする方法はCIでplugin_errorsを検査する記事にあります。

--plugin-dirで渡したディレクトリは、コピーされずその場で読み込まれます。開発中のプラグインを1セッションだけ試すなら、CLAUDE_CODE_PLUGIN_DIRSの使い方が参考になります。

落とし穴

リンクの先が正しくても、権限で落ちることがある

lsでファイルが読めても、Claude Codeを起動するユーザーの権限で途中のディレクトリを辿れなければEACCESになります。sudoで作ったディレクトリや、別ユーザーのホーム配下を指すリンクが典型です。

どの手から試すか

エラー文にPlugin not loadedとあるなら、.claude-pluginとplugin.jsonの実体を確かめます。could not be checkedなら、かっこ内のコードで切り分けます。ELOOPならリンクの循環、EACCESなら途中のディレクトリの実行権限、EIOとESTALEならマウントの状態です。

作者でない場合は、自分で中身を書き換えるより、メッセージ全文を作者に渡すほうが早く済みます。

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