app.json の plugins に1行足して、念のためビルド前に設定を読み込ませた夜のことを想像してみてください。画面に出るのは、次の赤い文字です。
PluginError: Failed to resolve plugin for module "expo-build-properties" relative to "/tmp/exp/probe". Do you have node modules installed?「node_modules は入れたはずなのに」と npm install をやり直し、キャッシュを消しても、同じ文言が返ってきます。手が止まる瞬間です。
Expo SDK 58 の空のプロジェクトを手元に作り、この文言が出る条件を一つずつ再現してみました。結論を先にお伝えします。この1行の文言は、原因が少なくとも3つあっても、ほぼ同じ顔で出ます。 だから「直す」より先に「どの原因かを切り分ける」順番を決めておくのが、いちばん早い近道でした。
まず、手元で確かめた環境
再現に使ったのは、create-expo-app の blank テンプレートです。Expo SDK 58(expo 58.1.14)、Node 22、npm install 直後の状態から始めました。
確認のコマンドは、アプリを起動せずに設定だけを読む次の1本です。
CI=1 npx expo config --type publicapp.json を読み込み、plugins を実際に解決して、最終的な設定を表示します。エミュレータも実機も要りません。plugins を触ったら、まずこれを叩く。個人開発で設定ファイルを触るたびに、私自身いまもこの1本から始めています。
エラーがなければ設定の JSON 風の出力が流れ、plugins に問題があればその場で止まります。
原因1・2・3: 同じ「Failed to resolve」の裏側
plugins に次の4通りを順に書いて、expo config の出力を比べました。
| plugins に書いたもの | 実際の状況 | 出た文言(冒頭) |
|---|---|---|
"expo-build-properties" | パッケージが未インストール | Failed to resolve plugin for module |
"expo-status-barr" | 綴りの誤り(存在しない名前) | Failed to resolve plugin for module |
"./plugins/withFoo" | ローカルのファイルがまだ無い | Failed to resolve plugin for module |
"left-pad" | パッケージはあるが入れていない | Failed to resolve plugin for module |
見事に全部同じです。つまりこの文言が教えてくれるのは「その名前で、そのプロジェクトからは見つからなかった」ということだけで、理由までは語ってくれません。
文言の最後にある Do you have node modules installed? は、親切なようでいて、綴りの誤りやファイルの置き忘れの場合には的外れな問いかけになります。この問いに素直に従って npm install を何度も回すと、時間だけが過ぎてしまいます。
見つからなかった理由は、次の順に確かめるのが無駄がありません。
- 綴り:
pluginsに書いた文字列を、npm のパッケージ名と1文字ずつ見比べます。公式のものはexpo-で始まる名前がほとんどです。 - インストール:
package.jsonのdependenciesにその名前があるかを見ます。 - ローカルファイル:
./で始まる場合は、app.jsonから見た相対パスにファイルがあるかを確かめます。
3つ目の確認で、手元では一つ発見がありました。ローカルのプラグインは、拡張子を付けずに書きます。"./plugins/withFoo" と書けば withFoo.js が拾われ、ファイルを置いた途端にエラーは消えました。.ts のファイルも、今回の環境ではそのまま解決できました。
直し方は、インストール漏れなら次の1行です。
npx expo install expo-build-propertiesnpm install ではなく expo install を使うのは、SDK に合った版を選んでくれるからです。実行後の package.json には "expo-build-properties": "~58.0.11" と入りました。そのうえで expo config を叩き直すと、plugins がそのまま表示され、エラーは消えました。
原因4: 見つかったけれど、プラグインではなかった
次は、文言が変わるパターンです。plugins に "react" と書いてみました。react は確かにインストール済みです。
PluginError: Package "react" does not contain a valid config plugin. Module must export a function from file: /tmp/exp/probe/node_modules/react/index.js今度は「Failed to resolve」ではありません。パッケージは見つかったのに、Expo のプラグインとして使える関数を持っていないという意味です。
起きやすいのは、普通のライブラリ名を、そのまま plugins に写してしまったときです。ネイティブ側の設定を書き換える必要があるライブラリだけが、app.plugin.js を持っています。たとえば expo-status-bar の中には app.plugin.js があり、react にはありません。
迷ったときは、そのパッケージの README に「app.json の plugins に追加する」という手順があるかを探します。書かれていなければ、plugins に入れる必要はありません。
ローカルのファイルがオブジェクトを export していた場合にも、近い文言になります。
PluginError: Plugin "./plugins/withObj" must export a function from file: /tmp/exp/probe/plugins/withObj.js.こちらは module.exports = (config) => config のように、関数を export すれば通ります。
原因5: プラグインの中で起きた、ふつうのエラー
最後に、見分けておきたい1パターンがあります。ローカルのプラグインの中で例外を投げると、こうなりました。
Error: boom in plugin
at module.exports (/tmp/exp/probe/plugins/withBoom.js:1:31)
at withStaticPlugin (...)PluginError という接頭辞も、「resolve」という単語もありません。見つけて、実行して、その中身が落ちているということです。この場合は node_modules も綴りも関係なく、スタックトレースの最初に出てくる自分のファイルの行を読めば足ります。
ここまでを、読む順番にまとめておきます。
| 見えた文言 | 意味 | 次にすること |
|---|---|---|
| Failed to resolve plugin for module "X" | 名前 X で見つからない | 綴り → dependencies → ファイルの有無の順に確認 |
| does not contain a valid config plugin | パッケージはあるがプラグインではない | README に plugins への追記手順があるか確認 |
| must export a function | ファイルはあるが export が関数ではない | 関数を export するよう直す |
| 上記以外の Error(PluginError なし) | プラグインの中で例外 | スタックの自分のファイルの行を読む |
今回の線引き
エラー文の最後の問いかけに従って依存関係を入れ直すのは、原因がインストール漏れのときにしか効きません。綴りやファイルの置き忘れには、何度回しても届きません。私はいま、文言の冒頭の2〜3単語で原因を分けてから動くようにしています。ここに気づいたのは、再現のために4通りを並べて見比べたあとでした。
エラー文の最後の一文ではなく、最初の一文で原因を分けます。 この線引きだけは、急いでいる日ほど守るようにしています。
なお、Rork で作ったプロジェクトを自分の環境に持ってきて plugins を足した場合も、見る場所は同じです。AI が生成した設定でも、手で足した設定でも、expo config に通せば同じ判定が返ってきます。
今回の検証は空のプロジェクト1つで、モノレポ構成や、app.config.js から plugins を組み立てる構成までは確かめていません。そうした構成では、エラー冒頭に出る relative to "…" のパスが、探しに行った場所のヒントになるはずです。
最初の一歩として、いま手元の app.json に plugins があるなら、一度 CI=1 npx expo config --type public を叩いてみていただければと思います。何も出なければ、それが一番良い知らせです。
もし SDK のパッチ更新のたびに依存のずれが気になる方には、expo install の推奨がどれだけ動くかを数えた別の記事もあります。