Picture the moment: you add one line to plugins in app.json, and before kicking off a build you let Expo read the config once. This is what comes back.
PluginError: Failed to resolve plugin for module "expo-build-properties" relative to "/tmp/exp/probe". Do you have node modules installed?You reinstall, you clear the cache, and the same message returns. It's a very specific kind of stuck.
I reproduced this in a blank Expo SDK 58 project (expo 58.1.14, Node 22, straight after npm install) and tried each way of triggering it. The short version: one message can come from several different causes, so deciding the order of checks matters more than any single fix.
The one command I check with
To read the config without launching anything:
CI=1 npx expo config --type publicIt loads app.json, actually resolves every plugin, and prints the final config. No emulator, no device. As an indie developer I touch config files often, and I still run this before anything else whenever plugins changes.
Three causes, one message
I put four different things in plugins, one at a time:
| What I put in plugins | What was actually wrong | Message (start) |
|---|---|---|
"expo-build-properties" | Package not installed | Failed to resolve plugin for module |
"expo-status-barr" | Typo, no such package | Failed to resolve plugin for module |
"./plugins/withFoo" | Local file didn't exist yet | Failed to resolve plugin for module |
"left-pad" | Package exists on npm, not installed here | Failed to resolve plugin for module |
All identical. The message only says "nothing was found under that name from this project"; it doesn't say why. The trailing question, Do you have node modules installed?, is misleading when the real problem is a typo or a missing file. Following it by re-running npm install again and again only burns time.
The order I check, cheapest first:
- Spelling. Compare the string in
pluginsagainst the package name, character by character. - Installed. Is the name in
dependenciesinpackage.json? - Local file. If it starts with
./, does the file exist relative toapp.json?
One detail on local plugins: leave the extension off. "./plugins/withFoo" picked up withFoo.js the moment I created it, and a .ts file also resolved as-is in this environment.
For a missing install, this is the fix:
npx expo install expo-build-propertiesI use expo install rather than npm install so the version matches the SDK; it wrote "expo-build-properties": "~58.0.11". After that, expo config printed the plugin entry and the error was gone.
Cause four: found, but not a plugin
Next, a different message. I put "react" in plugins, which is installed:
PluginError: Package "react" does not contain a valid config plugin. Module must export a function from file: /tmp/exp/probe/node_modules/react/index.jsNow it's not "Failed to resolve". The package was found; it just doesn't export a config plugin. This tends to happen when an ordinary library name gets copied into plugins. Only libraries that need to change native settings ship an app.plugin.js: expo-status-bar has one, react doesn't. When in doubt, check whether the package's README tells you to add it to plugins. If it doesn't, you don't need it there.
A local file that exports an object gets a close cousin:
PluginError: Plugin "./plugins/withObj" must export a function from file: /tmp/exp/probe/plugins/withObj.js.Export a function such as module.exports = (config) => config and it passes.
Cause five: an ordinary error inside your plugin
When I threw an exception inside a local plugin, the output was different again:
Error: boom in plugin
at module.exports (/tmp/exp/probe/plugins/withBoom.js:1:31)
at withStaticPlugin (...)No PluginError, no "resolve". Expo found the plugin and ran it, and your code failed. Node modules and spelling are irrelevant here; read the first line of the stack trace that points at your own file.
Here's the reading order in one place:
| What you see | What it means | Next step |
|---|---|---|
| Failed to resolve plugin for module "X" | Nothing found under name X | Spelling, then dependencies, then file existence |
| does not contain a valid config plugin | Package found, not a plugin | Check the README for a plugins entry |
| must export a function | File found, export isn't a function | Export a function |
| Any other Error (no PluginError) | Exception inside the plugin | Read the stack line in your own file |
The rule I took from this
Re-running npm install because the message told me to only helps when the cause is a missing install. It never reaches a typo or a missing file. So I now sort the cause by the first few words of the message before touching anything.
Split by the first line of the error, not the last. I try to hold to that even when I'm in a hurry.
The same check applies if you brought a Rork-generated project into your own environment and added plugins there: expo config returns the same verdict for generated and hand-written config.
A caveat: this was one blank project. I didn't test monorepos or setups that build plugins inside app.config.js. In those, the relative to "…" path in the error is the first hint about where Expo looked.
If plugins is non-empty in your app.json right now, run CI=1 npx expo config --type public once. If it prints nothing alarming, that's the best news you'll get today.
And if patch-level SDK updates make you wary of dependency drift, I counted how many expo install recommendations move with each patch in another article.