Most Expo errors fall into one of four layers: Metro couldn't bundle your code, JavaScript crashed at runtime, a native module is missing from the binary you're running, or the native build (Xcode or Gradle) failed. Naming the layer first is half the fix.
Here are the ten errors that come up most, what they mean and how to fix them. If yours isn't here, paste it into the free React Native & Expo Error Fixer and it will explain the cause and the fix.
Before anything else, two commands fix a surprising number of problems:
npx expo start --clear # restart Metro with an empty cache
npx expo-doctor # check your project for known issues
1. Unable to resolve module
Unable to resolve module ./components/Header from App.tsx:
./components/Header could not be found within the project
Layer: Metro.
Why: the import path doesn't match a real file, the package isn't installed, or Metro is serving a stale cache after you moved or renamed something. A common one: the file is header.tsx but you import ./Header. macOS ignores case, but Linux CI and EAS Build don't.
Fix:
- Check the path and the exact capitalisation.
- For a package, install it with
npx expo install <package>so you get the version that matches your SDK. - Restart with
npx expo start --clear. - If you use path aliases like
@/components, make sure they're set intsconfig.json(Expo readspathsfrom it).
Paste your resolve error into the Error Fixer →
2. "main" has not been registered
Invariant Violation: "main" has not been registered.
Layer: JavaScript, at startup.
Why: the app's entry file never ran registerRootComponent, usually because something crashed while the first modules were loading, or Metro is running from the wrong folder (common in monorepos).
Fix:
- Scroll up: the real error is usually printed just above this one. Fix that first.
- Run
npx expo startfrom the app's own folder. - If you changed the entry point, check the
mainfield inpackage.json(expo-router/entryfor Expo Router, or a file that callsregisterRootComponent(App)).
3. Text strings must be rendered within a <Text> component
Error: Text strings must be rendered within a <Text> component.
Layer: JavaScript, at render.
Why: a raw string or number is sitting directly inside a View. The sneaky version is a conditional: {count && <Badge />} renders the number 0 when count is 0.
Fix:
- Wrap text in
<Text>. - Make conditionals return a boolean:
{count > 0 && <Badge />}or{!!count && <Badge />}. - Look for stray characters, like a space or
;left between JSX tags.
4. VirtualizedLists should never be nested inside plain ScrollViews
VirtualizedLists should never be nested inside plain ScrollViews
with the same orientation because it can break windowing and other functionality
Layer: JavaScript (a warning, but a real performance problem).
Why: a FlatList inside a vertical ScrollView renders every row at once, so you lose the virtualization that makes lists fast.
Fix: remove the outer ScrollView and move the content above and below the list into ListHeaderComponent and ListFooterComponent. If you need several lists on one screen, use a SectionList.
5. Native module not found in Expo Go
Error: Cannot find native module 'ExpoSomething'
Invariant Violation: TurboModuleRegistry.getEnforcing(...): 'RNSomething' could not be found.
Layer: native module.
Why: Expo Go only contains the native code for the libraries bundled into it. A library with its own native code that isn't one of them will install fine and then crash at runtime.
Fix: create a development build, which is your own version of Expo Go with your libraries compiled in:
npx expo install expo-dev-client
eas build --profile development --platform ios # or android
If you're already on a development build and see this, you added the library after building. Rebuild.
6. Element type is invalid
Element type is invalid: expected a string (for built-in components)
or a class/function (for composite components) but got: undefined.
Layer: JavaScript.
Why: an import resolved to undefined. Almost always a default/named import mix-up: import Header from './Header' when the file does export function Header, or the other way around. Circular imports can cause it too.
Fix: match the import to the export (import { Header } from './Header' for a named export). The error's component stack tells you which component was undefined.
7. Too many re-renders
Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
Layer: JavaScript.
Why: state is set during render. The classic: onPress={setOpen(true)} calls the function immediately instead of passing it.
Fix:
- Pass a function:
onPress={() => setOpen(true)}. - Move state updates that depend on props into an event handler or a
useEffectwith the right dependencies. - The related "Maximum update depth exceeded" usually means a
useEffectupdates a value that's in its own dependency list.
8. Network request failed
TypeError: Network request failed
Layer: JavaScript, but the cause is usually the network setup.
Why: on a phone, localhost is the phone, not your computer. Other causes: plain http:// URLs, which Android blocks by default (and iOS App Transport Security restricts), or a server that isn't reachable from the device's network.
Fix:
- Use your computer's LAN IP (for example
http://192.168.1.20:3000) or a tunnel, and keep the phone on the same Wi-Fi. - Use
httpsfor anything outside local development. - Read the URL from an environment variable (
EXPO_PUBLIC_API_URL) so dev and production can differ.
9. Package versions don't match your Expo SDK
The following packages should be updated for best compatibility with the installed expo version:
react-native-reanimated@3.x.x - expected version: ~3.y.y
Layer: everywhere. Mismatched versions cause crashes at runtime and failures at build time.
Why: a library was installed with npm install instead of npx expo install, or the SDK was upgraded without updating its packages.
Fix:
npx expo install --fix
npx expo-doctor
When upgrading the SDK, follow the release notes and run npx expo install expo@^<version> --fix.
10. EAS Build failed
Gradle build failed with unknown error. See logs for the "Run gradlew" phase for more information.
or on iOS, a failed "Install pods" or "Run fastlane" phase.
Layer: native build.
Why: the last line rarely says much. The real cause is further up in the phase that failed: often a version mismatch (see #9), a config plugin that needs a setting you haven't added, or credentials and signing.
Fix:
- Open the failed phase in the EAS build page and search for the first
error:line. - Run
npx expo-doctorand fix what it reports. - Reproduce locally with
npx expo prebuild --cleanand thennpx expo run:androidornpx expo run:ios, which is much faster to iterate on than cloud builds.
Paste the failed build log into the Error Fixer →
A debugging routine that works for most of these
- Read the first error, not the last line. Red screens and build logs often end with something generic.
- Name the layer: Metro, JavaScript, native module or native build.
- Check how you run the app. Expo Go, a development build and a bare project behave differently.
-
Clear caches with
npx expo start --clearbefore deeper changes. -
Align versions with
npx expo install --fix.
If you're stuck on an error that isn't in this list, the free React Native & Expo Error Fixer explains it in plain English: paste the error, choose where it happens and how you run the app, and it gives you the likely causes and the fix. It's made by RapidNative, where I work.
What's the Expo error that has cost you the most time? Drop it in the comments.
Top comments (0)