「packages are not installed」と出るClaude Codeプラグインの対処
/pluginやclaude plugin listに出る「are not installed」と「were not installed, because」は別物です。前者は再試行で直り、後者は理由を取り除くまで直りません。
/pluginやclaude plugin listでプラグインの横に「The packages it lists are not installed」と出ているなら、そのプラグインのnode_modulesが空のままです。プラグイン自体は読み込まれますが、足りないパッケージを必要とする部分は動かないかもしれません。
注記は2種類あります。文言の違いで、次にやることがまったく変わります。
2種類の注記の違い
are not installed
インストールは走れる状態なのに、失敗やタイムアウトで終わらなかった。claude plugin updateをやり直せば直ることがあります。
were not installed, because ...
インストール自体が走れない状態で、理由が文中に書かれる。更新しても、その理由が残る限り入りません。
この注記はいつ出るのか
Claude Codeはマーケットプレイスのプラグインをキャッシュへコピーするとき、そのプラグインがpackage.jsonに宣言したnpm・Bunのパッケージも同じ場所に入れます。注記が出るのは、有効なプラグインのキャッシュにlockfileと、ランタイム依存を列挙したpackage.jsonの2つがあるのに、node_modulesが無いときです。
つまり、依存を持たないプラグインや、lockfileを同梱していないプラグインには出ません。どちらも「入れる対象がそもそも無い」状態です。
依存のインストールが走るのは、キャッシュ側に新しいバージョンのディレクトリを作る次の3つの場面です。
- プラグインを入れたとき
- プラグインが新しいバージョンに更新されたとき
- 新しいマシンなどで、有効なプラグインがまだキャッシュに無いままセッションを始めたとき
「are not installed」の直し方
こちらは再試行が効く側です。注記に出ているコマンドをそのままシェルで実行します。
claude plugin update formatter@my-marketplace/pluginからそのプラグインを更新しても同じ再試行になります。再試行が失敗した場合は、出力にその原因が出ます。ネットワーク制限が疑わしいなら、許可すべきホストはネットワーク設定の要件のページにまとまっています。
インストールには60秒のタイムアウトがあり、超えると失敗扱いです。回線が細い環境や大きな依存を持つプラグインで「毎回途中で終わる」場合は、失敗の原因として最初に疑う箇所です。
更新したのに「最新です」と返るとき
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFICを設定していると、claude plugin updateも/pluginも再試行をスキップし、プラグインは最新バージョンだと報告します。注記が消えないのに「最新です」としか返らないなら、まずこの環境変数を疑ってください。CI用の設定ファイルや社内の共通シェル設定に入っていることがあります。
この変数の影響範囲はDISABLE_NONESSENTIAL_TRAFFICの解説にまとめています。一時的に外して更新し、入ったら戻す、という運用が現実的です。
「were not installed, because ...」の直し方
理由が文中に書かれているので、まずそれを読みます。公式が例に挙げる理由は2系統です。
| 理由の種類 | 例 | 直す人 |
|---|---|---|
| lockfileが対象外 | 例Yarn、pnpm、bun.lockb | 直す人プラグインの作者 |
| パッケージマネージャーが無い | 例lockfileに対応するツールがPCに未導入 | 直す人利用者 |
どちらの場合も、claude plugin updateを繰り返しても入りません。理由が残っているかぎり、更新は何も変えないからです。
lockfileが理由のとき
利用者側にできることは、作者に伝えることです。yarn.lockとpnpm-lock.yamlは読まれず、bun.lockbはBunのバイナリ形式で検査できないためスキップされます。作者は、テキスト形式のbun.lockかnpmのlockfileに置き換える必要があります。
置き換え後も読まれない形式があります。npmのlockfileはlockfileVersionが2か3(npm 7以降が書き出す形式)、bun.lockは2以下でなければなりません。
パッケージマネージャーが理由のとき
こちらは自分で直せます。lockfileに対応するツールをPATHに入れ、そのあとでプラグインを更新します。
複数のlockfileが同居しているプラグインでは、bun.lock、npm-shrinkwrap.json、package-lock.jsonの順で最初に見つかったものが使われます。しかも、そのツールが無いからといって別のlockfileに切り替えることはしません。bun.lockとpackage-lock.jsonの両方を同梱したプラグインは、Bunが無いマシンではpackage-lock.jsonがあっても止まります。
手元のキャッシュで切り分ける
注記の文言だけで決めきれないときは、キャッシュ側のディレクトリを見ます。プラグインは~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/にコピーされます(CLAUDE_CODE_PLUGIN_CACHE_DIRで場所を変えている場合はその下)。
cd ~/.claude/plugins/cache/my-marketplace/formatter/1.2.0
ls package.json bun.lock bun.lockb yarn.lock \
pnpm-lock.yaml package-lock.json npm-shrinkwrap.json 2>/dev/null
ls -d node_modules見る点は3つです。
package.jsonと対応するlockfileが両方あるか- あるlockfileが、読まれない
yarn.lock・pnpm-lock.yaml・bun.lockbではないか node_modulesが無いまま、package.jsonの依存とlockfileの内容が食い違っていないか
最後の点は、依存の一覧が食い違うとインストール自体がスキップされるためです。レジストリのパッケージ以外(git、GitHub、フォルダ、workspace、リンク)を依存に含む場合や、npmのoverrides、BunのpatchedDependenciesを使う場合も、インストールは行われません。ここまでくると「再試行」の話ではなく、プラグインの作りの話です。
制限ごとに見える症状
インストールは、プラグインや依存のコードが一切動かない形で実行されます。そのため、次の条件に当たると失敗や未導入になります。
| 当たった制限 | 起きること | 見分け方 |
|---|---|---|
lockfileのダウンロードリンクがhttpsでない | 起きること入らない。例外は利用者自身の既定のnpmレジストリを指すリンクだけ | 見分け方lockfileのresolved欄がhttp://や社内ホストになっていないか |
| ライフサイクルスクリプトが無効 | 起きることダウンロードはされるが、postinstallなどでのビルドは走らない | 見分け方依存は入っていても、ビルドが要るパッケージの読み込みで失敗しうる |
| 別フォルダで実行される | 起きることプラグインの.npmrc、.env、bunfig.tomlは読まれない | 見分け方プラグイン側の設定に頼った認証付きレジストリは通らない |
2行目は特に紛らわしい点です。「入った」のに動かないときは、注記ではなく依存側の仕組みを疑うことになります。インストールの成否と、入ったパッケージが実際に動くかどうかは別の問題です。
キャッシュの仕組みそのものはプラグインのキャッシュとファイル解決で、依存インストールの制限は同記事の該当節で詳しく扱っています。
ローカルで開発中のプラグインは対象外
注記とは別に、そもそも依存が入らない読み込み方があります。キャッシュへコピーされず、元の場所から直接読み込まれるプラグインです。
--plugin-dirで指したディレクトリ- ローカルパスで追加したマーケットプレイスの、相対パスのプラグイン
元の場所から読み込まれるこれらのプラグインでは、ソースのディレクトリに依存がインストールされません。編集は次のセッション開始か/reload-pluginsで反映されますが、node_modulesは自分で用意する必要があります。ソースのディレクトリでnpm installするか、次節のようにフックからCLAUDE_PLUGIN_DATAへ入れます。
--plugin-urlのアーカイブや--plugin-dirに渡した.zipは別の扱いで、セッション用の一時ディレクトリに展開されてから読み込まれ、こちらもキャッシュを通りません。
なお、インストールはlockfileが固定した正確なバージョンだけを入れます。package.jsonの依存を書き換えてlockfileを更新し忘れると、両者の一覧が食い違ってインストールごとスキップされます。依存を足したときは、lockfileも作り直してから配るのが前提です。
自動インストールが使えないときの代替
自動インストールは設定や環境変数で止められず、効かない条件を外すしかありません。ネイティブモジュールのビルドが要る依存、Pythonの依存、Yarnやpnpmでロックしたプラグインは、フックでCLAUDE_PLUGIN_DATAに入れる方法で対処します。
CLAUDE_PLUGIN_DATAはアップデートをまたいで残るディレクトリで、~/.claude/plugins/data/<id>/に解決されます。プラグインの作者はSessionStartフックで、初回とpackage.jsonが変わったあとの2回だけnpm installを走らせられます。
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "diff -q \"${CLAUDE_PLUGIN_ROOT}/package.json\" \"${CLAUDE_PLUGIN_DATA}/package.json\" >/dev/null 2>&1 || (cd \"${CLAUDE_PLUGIN_DATA}\" && cp \"${CLAUDE_PLUGIN_ROOT}/package.json\" . && npm install) || rm -f \"${CLAUDE_PLUGIN_DATA}/package.json\""
}
]
}
]
}
}これは公式の例に沿った形です。入れ終わったあと、MCPサーバーはenvのNODE_PATHに${CLAUDE_PLUGIN_DATA}/node_modulesを指定して、そこからパッケージを読めます。
この方式なら、プラグインのルートに入れる自動インストールの制約(スクリプト無効、タイムアウトなど)の外でパッケージを用意できます。ただし、フックでnpm installを走らせるのはライフサイクルスクリプトも動く別の経路です。信頼できないプラグインの依存を、この方式で入れる判断は慎重に行ってください。
注記が出たときに確かめる順番
注記が出たときの確認順
- Q注記は「are not installed」か?答えが「were not installed, because」の理由を読む答えがはい
理由が文中に書かれているので、lockfileの種類なら作者へ連絡します。パッケージマネージャー未導入なら自分で入れてから更新します。
- Q`claude plugin update`で注記が消えたか?答えが「最新です」とだけ返る答えがはい
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFICを外して、もう一度更新します。
それでも直らない場合
キャッシュ側のlockfileと依存の種類を確認し、自動インストールの対象外なら作者にフック方式を依頼します。
再発させないために
プラグインを配る側が揃えておくと、利用者に注記が出にくくなる条件は次のとおりです。
- npmのlockfile(
package-lock.json)を同梱する。Yarn・pnpmのlockfileは置き換える - npm経由で配るなら
npm-shrinkwrap.jsonにする。npmは公開パッケージからpackage-lock.jsonを除外する - 依存はレジストリのパッケージを正確なバージョンで固定する
- ネイティブビルドが要る依存は、自動インストールに頼らずフックで用意する
導入済みプラグインの状態を一覧で棚卸ししたいときは、claude plugin list --jsonの棚卸しが使えます。プラグインどうしの依存の話はプラグイン依存関係の領分です。