Fix: Expo Not Working — Build Failing, Native Module Not Found, or EAS Build Error
Quick Answer
How to fix Expo issues — Expo Go vs development builds, native module installation with expo-modules-core, EAS Build configuration, bare workflow setup, and common SDK upgrade problems.
The Problem
A native module throws an error when running in Expo Go:
Error: Invariant Violation: `new NativeEventEmitter()` requires a non-null argument.Or the app crashes with a “module not found” error after installing a package:
Unable to resolve module 'expo-camera' from 'src/App.tsx':
None of these files exist: expo-camera/build/...Or an EAS Build fails with a cryptic Gradle or Xcode error:
> Task :app:processDebugResources FAILED
AAPT: error: resource attr/colorPrimary (aka com.myapp:attr/colorPrimary) not found.Or after upgrading the Expo SDK, features stop working:
Error: Cannot find native module 'ExpoCamera'Why This Happens
Expo has two distinct workflows with different constraints:
- Expo Go only supports Expo SDK modules — Expo Go is a pre-built app that includes a fixed set of native modules. Any package with custom native code that isn’t part of the Expo SDK won’t work in Expo Go. You need a development build for those packages.
- Native modules require a build step — JavaScript-only packages work immediately after
npm install. Native modules (anything withandroid/andios/directories) require runningnpx expo prebuildor rebuilding the native project. Without this, the module is installed but its native code isn’t linked. - EAS Build differences from local builds — EAS builds run in a clean CI environment. Environment variables, credentials, and native project modifications (patches, custom gradle scripts) must be explicitly configured.
- SDK upgrades change native dependencies — major Expo SDK upgrades update the underlying React Native version and native module APIs. Running
npx expo install(notnpm install) ensures version-compatible packages.
Fix 1: Use the Right Workflow for Your Needs
Expo Go — zero setup, great for prototyping, limited to Expo SDK modules:
# Create a new Expo project
npx create-expo-app MyApp
# Start dev server
npx expo start
# Scan QR code with Expo Go appDevelopment Build — custom native modules, most like production:
# Install expo-dev-client
npx expo install expo-dev-client
# Build a development client for your device
npx eas build --profile development --platform ios
# OR locally (requires Xcode/Android Studio):
npx expo run:ios
npx expo run:android
# Start the dev server (points to your dev build)
npx expo start --dev-clientWhen to use each:
| Feature | Expo Go | Development Build |
|---|---|---|
| Custom native modules | ❌ | ✅ |
| All Expo SDK packages | ✅ | ✅ |
| No build required | ✅ | ❌ |
| Production-like behavior | ❌ | ✅ |
| Over-the-air updates (EAS Update) | ✅ | ✅ |
Fix 2: Install Native Modules Correctly
Always use npx expo install instead of npm install for Expo packages — it picks the version compatible with your SDK:
# CORRECT — Expo-compatible version
npx expo install expo-camera expo-location expo-notifications
# WRONG — may install incompatible version
npm install expo-camera # Might install wrong version for your SDK
# Check if installed packages are compatible with current SDK
npx expo install --check
# Fix incompatible packages
npx expo install --fixAfter installing a native module, rebuild the native project:
# Option 1: Prebuild (managed workflow → bare workflow)
npx expo prebuild
# Creates android/ and ios/ directories with native code
# Option 2: Run directly (rebuilds automatically)
npx expo run:ios # Builds and runs on iOS simulator
npx expo run:android # Builds and runs on Android emulator/device
# Option 3: EAS Build
npx eas build --profile development --platform allVerify a package works with Expo:
# Check Expo compatibility
npx expo install --check some-package
# Look for the Expo config plugin
# expo-modules-core compatible packages have an app.json plugin or
# an expo-module.config.json in their packageFix 3: Configure app.json and Expo Plugins
Native modules often require config plugins to modify the native project:
// app.json
{
"expo": {
"name": "MyApp",
"slug": "my-app",
"version": "1.0.0",
"sdkVersion": "52.0.0",
"platforms": ["ios", "android"],
"plugins": [
// Simple plugin — string
"expo-camera",
// Plugin with options
["expo-camera", {
"cameraPermission": "Allow $(PRODUCT_NAME) to access your camera."
}],
// expo-notifications requires extensive config
["expo-notifications", {
"icon": "./assets/notification-icon.png",
"color": "#ffffff",
"sounds": ["./assets/notification.wav"]
}],
// Custom plugin
"./plugins/withCustomGradleConfig"
],
"ios": {
"bundleIdentifier": "com.yourcompany.myapp",
"buildNumber": "1",
"infoPlist": {
"NSLocationWhenInUseUsageDescription": "Used to show nearby restaurants."
}
},
"android": {
"package": "com.yourcompany.myapp",
"versionCode": 1,
"permissions": [
"ACCESS_FINE_LOCATION",
"CAMERA"
]
},
"extra": {
"eas": {
"projectId": "your-eas-project-id"
}
}
}
}After changing app.json plugins, re-run prebuild:
npx expo prebuild --clean # --clean removes and regenerates native dirs
# Then rebuild:
npx expo run:iosFix 4: Set Up EAS Build
# Install EAS CLI
npm install -g eas-cli
# Login to Expo account
eas login
# Initialize EAS in your project
eas build:configure
# Creates eas.json// eas.json
{
"cli": {
"version": ">= 12.0.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"ios": {
"simulator": true
}
},
"preview": {
"distribution": "internal",
"android": {
"buildType": "apk" // APK for internal distribution
}
},
"production": {
"ios": {
"credentialsSource": "remote"
},
"android": {
"buildType": "app-bundle" // AAB for Play Store
}
}
},
"submit": {
"production": {
"ios": {
"appleId": "[email protected]",
"ascAppId": "1234567890"
},
"android": {
"serviceAccountKeyPath": "./google-service-account.json"
}
}
}
}# Build for development
eas build --profile development --platform ios
# Build for all platforms
eas build --profile production --platform all
# Submit to app stores
eas submit --platform ios
eas submit --platform androidFix common EAS Build errors:
# "Gradle build failed" — often a dependency conflict
# In eas.json, add:
{
"build": {
"production": {
"android": {
"gradleCommand": ":app:bundleRelease",
"buildType": "app-bundle"
}
}
}
}
# Environment variables in EAS Build
eas secret:create --scope project --name API_URL --value https://api.example.com
# Or in eas.json:
{
"build": {
"production": {
"env": {
"APP_ENV": "production",
"API_URL": "https://api.example.com"
}
}
}
}Fix 5: Upgrade Expo SDK
Expo SDK upgrades are non-trivial but the CLI guides you:
# Check current SDK version
npx expo --version
# Upgrade to latest SDK
npx expo install expo@latest
# Install compatible versions of all Expo packages
npx expo install --fix
# For major version upgrades, use the upgrade command
npx expo upgrade
# Follow the prompts — it updates app.json sdkVersion and dependencies
# After upgrading, check for breaking changes
# https://expo.dev/changelogCommon post-upgrade fixes:
# Clear caches after upgrade
npx expo start --clear
# iOS:
npx expo run:ios --no-install # Skip pod install if already done
cd ios && pod install # Then manually run pod install
# Android:
cd android && ./gradlew clean
# If prebuild generates conflicts with manual native changes:
npx expo prebuild --clean # WARNING: overwrites ios/ and android/Fix 6: Over-the-Air Updates with EAS Update
Push JavaScript changes without a new app store submission:
# Install EAS Update
npx expo install expo-updates
# Configure
eas update:configure
# Push an update to the preview channel
eas update --branch preview --message "Fix login bug"
# Push to production
eas update --branch production --message "v1.2.0 release"// app.json — configure update channels
{
"expo": {
"updates": {
"url": "https://u.expo.dev/your-project-id",
"fallbackToCacheTimeout": 0,
"checkAutomatically": "ON_LOAD"
},
"runtimeVersion": {
"policy": "appVersion" // New native build required when app version changes
}
}
}// Manually check for updates in app
import * as Updates from 'expo-updates';
async function checkForUpdate() {
try {
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
await Updates.reloadAsync(); // Restart with new version
}
} catch (error) {
console.error('Update check failed:', error);
}
}Still Not Working?
“Module not found” after npx expo install — you installed the package but didn’t rebuild the native code. JavaScript-only packages work immediately, but packages with native code require npx expo run:ios or npx expo run:android to link the native modules. If using Expo Go, check if the package is supported — look for “Expo Go” compatibility on the package’s Expo documentation page.
App works on simulator but crashes on device — common causes: (1) permissions not declared in app.json — add required permissions to the ios.infoPlist or android.permissions sections, (2) architecture mismatch — physical iOS devices require arm64 builds, simulators use x86_64/arm64, (3) provisioning profile issues — check your Apple Developer account and EAS credentials.
Hermes engine errors — Expo 50+ uses Hermes by default. Hermes doesn’t support all JavaScript features (some Proxy and Reflect behaviors differ). If a package fails only on Hermes, check if the package has a known Hermes incompatibility. As a last resort, disable Hermes in app.json:
{
"expo": {
"jsEngine": "jsc" // Switch back to JavaScriptCore — not recommended
}
}For related React Native issues, see Fix: React Native Android Build Failed and Fix: React Native Metro Bundler Failed.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: React Native Reanimated Not Working — Worklet Error, useAnimatedStyle Not Updating, or Gesture Not Responding
How to fix React Native Reanimated issues — worklet rules, shared values, useAnimatedStyle, Gesture Handler setup, web support, Babel plugin configuration, and Reanimated 3 migration.
Fix: React Native Android Build Failed
How to fix React Native Android build failures — SDK version mismatches, Gradle errors, duplicate module issues, Metro bundler problems, and NDK configuration for common build errors.
Fix: React Native Metro Bundler Failed to Start or Bundle
How to fix React Native Metro bundler errors — unable to resolve module, EMFILE too many open files, port already in use, transform cache errors, and Metro failing to start on iOS or Android.
Fix: Deno PermissionDenied — Missing --allow-read, --allow-net, and Other Flags
How to fix Deno PermissionDenied (NotCapable in Deno 2) errors — the right permission flags, path-scoped permissions, deno.json permission sets, and the Deno.permissions API.