Expo/EAS build fails: "First argument of `require.context` should be a string" — `Invalid call: process.env.EXPO_ROUTER_APP_ROOT`

3 weeks ago 31
ARTICLE AD BOX

Problem summary:
When running eas build / npx expo update for Android, bundling fails with an error coming from expo-router that says the first argument of require.context must be a string. The app works in Expo Go but fails only during EAS/packaging. I tried adding expo-router/babel to babel.config.js (as recommended), but the error persists.

Sample image

https://i.sstatic.net/YjEJVSWx.png

Environment

OS: Windows 10/11 Node: v24.7.0 Expo CLI / EAS: using eas commands (build/update) Expo SDK: 54.0.30 expo-router: ~6.0.21 React Native: 0.81.5 Relevant deps: metro-react-native-babel-preset ^0.77.0

Commands that fail

npx expo export:embed --eager --platform android --dev false --reset-cache eas build (or eas update)

Full error (relevant parts)

Starting Metro Bundler SyntaxError: node_modules\expo-router\_ctx.android.js: node_modules\expo-router\_ctx.android.js:Invalid call at line 2: process.env.EXPO_ROUTER_APP_ROOT First argument of `require.context` should be a string denoting the directory to require. Error: node_modules\expo-router\_ctx.android.js:Invalid call at line 2: process.env.EXPO_ROUTER_APP_ROOT First argument of `require.context` should be a string denoting the directory to require. at transformJS ... @expo/metro-config\build\transform-worker\metro-transform-worker.js:337:23 at transformJSWithBabel ... @expo/metro-config\build\transform-worker\metro-transform-worker.js:487:18

Relevant config files (trimmed)

package.json "main": "expo-router/entry", "dependencies": { "expo": "^54.0.30", "expo-router": "~6.0.21", "metro-react-native-babel-preset": "^0.77.0", ... } app.json { "expo": { "plugins": ["expo-router", ...], "runtimeVersion": { "policy": "appVersion" }, "updates": { "url": "..." } ... } } babel.config.js (current) module.exports = { presets: ["module:metro-react-native-babel-preset"], plugins: [ "expo-router/babel", ["react-native-worklets/plugin", workletsPluginOptions] ], }; metro.config.js const { getDefaultConfig } = require("expo/metro-config"); const { withUniwindConfig } = require("uniwind/metro"); const config = getDefaultConfig(__dirname); module.exports = withUniwindConfig(config, { cssEntryFile: "./src/global.css" });

What I expect

The bundler should inline/transform the process.env.EXPO_ROUTER_APP_ROOT used by expo-router so require.context gets a string literal and bundling succeeds.

What I tried

Added expo-router/babel plugin to babel.config.js. Cleared Metro cache (--reset-cache), ran expo-doctor (all checks passed). Tried setting the env var manually before export (PowerShell: $env:EXPO_ROUTER_APP_ROOT="src/app"). Verified app runs fine in Expo Go (so runtime routes work locally).

Questions / specific help requested

Why is process.env.EXPO_ROUTER_APP_ROOT not being inlined during the bundling step, even after adding expo-router/babel to Babel config? Are there known issues with Node v24 or certain Metro/Babel plugin orders that would prevent the transform? Should I modify Metro config to explicitly include a transform plugin, or is there a recommended order/placement for expo-router/babel? What additional logs or minimal repro would be most helpful to debug this further?

Minimal reproduction guidance

A minimal repo that uses expo-router with a root at app and runs npx expo export:embed --eager --platform android --dev false --reset-cache should surface the same error (file-based routing and main: "expo-router/entry").

Tags

expo, expo-router, metro, babel, react-native, eas, bundling

If you want, I can also create a minimal reproduction repo and confirm whether ordering of Babel plugins or Metro config changes resolve this. Would you like me to do that? 🔧

Read Entire Article