What this usually means
Expo SDK upgrades are not drop-in replacements. Each SDK version pins specific versions of React, React Native, and a set of built-in native modules. When you upgrade, package.json may still reference old peer dependencies, lockfiles may have stale resolutions, and third-party libraries may not yet support the new SDK target. The underlying cause is almost always a version mismatch between what Expo expects and what your project declares—either in package.json, yarn.lock, or native config files like android/build.gradle or ios/Podfile.
The first ten minutes — establish facts before touching code.
- 1Run `npx expo doctor --fix-dependencies` to auto-detect and fix version mismatches
- 2Check the output of `npx expo config --type public` for any 'versionMismatch' warnings
- 3Inspect package.json: compare `expo`, `react`, `react-native`, and `react-native-unimodules` versions against the target SDK's compatibility table
- 4Delete node_modules and lockfiles, then reinstall with `npx expo install --fix`
- 5If using EAS Build, review the build log for lines containing 'unsatisfied constraint' or 'conflict'
The specific files, logs, configs, and dashboards that usually own this bug.
- searchpackage.json – especially the `expo`, `react`, `react-native` versions
- searchapp.json / app.config.js – check `expo.sdkVersion` and `expo.platforms`
- searchnode_modules/expo/package.json – to confirm installed Expo version
- searchios/Podfile.lock or android/app/build.gradle – for native dependency conflicts
- searchEAS Build logs (expo.dev/accounts/.../builds/) – search for 'error: ' or 'conflict'
- searchYarn/NPM lockfile – look for multiple versions of the same package
- searchExpo Status Page (status.expo.dev) – verify no ongoing SDK incident
Practical causes, not theory. These are the things you will actually find.
- warningRunning `npm install` instead of `expo install` after upgrade, breaking peer dependencies
- warningThird-party library not updated for the new SDK (e.g., react-native-maps v0.x vs SDK 50)
- warningCustom native modules (like expo-modules-core) pinned to old version in resolutions field
- warningLockfile corruption: yarn.lock or package-lock.json not regenerated after upgrade
- warningiOS Podfile not updated: missing or duplicate 'use_expo_modules!' or 'use_frameworks!'
- warningJavaScript bundler cache: stale Metro bundle from previous SDK
Concrete fix directions. Pick the one that matches your root cause.
- buildRun `npx expo install --fix` to align all Expo packages with the current SDK
- buildIf a library is incompatible, use `expo install` to let Expo pick a compatible version, or fork and patch
- buildFor iOS CocoaPods: run `cd ios && pod repo update && pod install --repo-update`
- buildClear Metro cache: `npx expo start -c` (or `react-native start --reset-cache`)
- buildFor stubborn native errors, create a new project with `npx create-expo-app@latest` and migrate files manually
- buildUse `npx expo customize` to regenerate default babel.config.js and metro.config.js
A fix you cannot prove is a guess. Close the loop.
- verifiedRun `npx expo doctor --fix-dependencies` and confirm zero warnings
- verifiedBuild successfully on both platforms: `npx expo run:ios` and `npx expo run:android`
- verifiedTest on a physical device with Expo Go, then with a development build
- verifiedRun `npx expo config --type public` and verify the 'sdkVersion' matches your target
- verifiedCheck that all third-party libraries appear in `expo install --check` output as compatible
Things that make this bug worse or harder to find.
- warningBlindly running `npm update` or `yarn upgrade` after `expo upgrade` – always use `expo install`
- warningIgnoring the 'expo doctor' warnings – they are almost always the root cause
- warningPatching node_modules directly instead of using `expo install` or patch-package
- warningDeleting ios/ or android/ folders without backing up custom native code
- warningAssuming Expo Go will work after an SDK upgrade without testing a development build first
SDK 49 to 50 Upgrade: 'react-native-maps' Crashes on iOS
Timeline
- 09:00Run `npx expo upgrade` from SDK 49 to 50 in the main branch
- 09:05`expo doctor` shows 3 warnings: react-native-maps, react-native-gesture-handler, expo-location
- 09:10Run `npx expo install --fix` – fixes gesture-handler and location, but maps warning persists
- 09:15iOS build fails on EAS: 'react-native-maps requires RCTBridge'
- 09:20Check package.json: react-native-maps is pinned to 0.31.1 (not compatible with RN 0.73)
- 09:30Search expo docs: react-native-maps 1.8.0+ required for SDK 50
- 09:35Run `npx expo install react-native-maps@1.8.0` – installs successfully
- 09:40Rebuild iOS on EAS – succeeds. Test on device – maps render correctly.
We were migrating our food delivery app from Expo SDK 49 to 50. After running `expo upgrade`, I got a few dependency warnings but thought `--fix` would handle them. It did for most, but react-native-maps was still flagged. I ignored it because the Android build passed.
Then the iOS build failed on EAS with an obscure native error about RCTBridge. I spent an hour chasing down CocoaPods issues, cleaning derived data, and even resetting the simulator. Nothing worked. Finally, I checked the react-native-maps changelog and saw they dropped support for React Native <0.73 in version 1.8.0.
We were pinned to 0.31.1. Running `npx expo install react-native-maps@1.8.0` fixed everything. The lesson: never trust a partial fix – run `expo doctor` until it's green, and check library compatibility tables before assuming native errors are infrastructure problems.
Root cause
react-native-maps version 0.31.1 incompatible with React Native 0.73 bundled in SDK 50.
The fix
Run `npx expo install react-native-maps@1.8.0` to update to a compatible version.
The lesson
After an SDK upgrade, run `expo doctor --fix-dependencies` and manually verify any warnings. Library version mismatches often surface as native build errors, not JavaScript errors.
When you run `expo upgrade`, Expo's CLI updates your app.json sdkVersion and modifies package.json to point to the new Expo package version. However, it does not automatically update all transitive dependencies. The critical versions that must align are: expo, react, react-native, react-native-unimodules (if present), and expo-modules-core. These are pinned to specific minor versions per SDK release.
The `npx expo install` command uses a compatibility table (published at https://exp.host/--/api/v2/versions/latest) to resolve the correct versions. Running `npm install` or `yarn add` bypasses this table, often pulling incompatible versions. Always use `expo install` for any package that starts with 'expo-' or is listed in Expo's bundled packages.
EAS Build logs are verbose but contain key signals. Search for: 'unsatisfied constraint', 'conflict', 'cannot find module', or 'use_frameworks!'. On iOS, look for lines starting with '[!]' or 'CocoaPods could not find compatible versions'. On Android, look for 'A problem occurred evaluating project' or 'Failed to resolve: com.google.android.gms'.
A common pattern is Podfile.lock referencing an old version of a pod that the new SDK expects to be updated. The fix: delete ios/Podfile.lock and run `pod install --repo-update`. For Android, invalidate caches by running `cd android && ./gradlew clean`.
`npx expo doctor --fix-dependencies` is your first line of defense, but it doesn't catch everything. It checks peer dependency ranges but may miss cases where a library's native code changed API surfaces. After running doctor, always do a full build on both platforms.
If doctor passes but you still see issues, use `npx expo config --type public` to inspect the resolved configuration. Look for 'versionMismatch' or 'invalidNativeModule' warnings. Also check the 'modules' array – any missing module that your code imports will cause a runtime crash.
If your project uses custom native modules (via expo-modules-api or plain React Native native modules), SDK upgrades can break them due to changes in the native module registry. For Expo Modules, ensure your module's `expo-module.config.json` targets the correct SDK version. For plain native modules, you may need to update `ReactNativeHost` or `RCTBridgeDelegate` implementations.
A pragmatic approach: create a fresh Expo project with `npx create-expo-app@latest`, then copy over your custom module code and test it in isolation. This isolates the upgrade issue from your app logic.
Stale caches are a frequent source of undiagnosed issues. Metro bundler caches JavaScript bundles; clear with `npx expo start -c`. Gradle caches Android native builds; run `cd android && ./gradlew cleanBuildCache`. CocoaPods caches pod specs; run `pod cache clean --all`.
After an SDK upgrade, always clean caches before your first build. Failure to do so can result in errors like 'Native module cannot be null' or 'No module named ...' even after correct package installation.
Frequently asked questions
Why does `expo install --fix` not fix all my dependency issues?
`expo install --fix` only adjusts versions of packages that Expo manages (like expo-* packages and peer dependencies it knows about). Third-party libraries that are not in Expo's compatibility table (e.g., some community modules) may not be updated. You may need to manually check their compatibility with the new SDK and use `expo install <package>@<version>` to update them.
How do I find which version of a library is compatible with my Expo SDK?
Check the library's changelog or GitHub releases for React Native version requirements. For Expo-specific libraries, refer to the Expo documentation page for that library – it usually lists compatible SDK versions. Alternatively, run `npx expo install <package> -- --help` to see available versions, or use the Expo SDK Version Reference at https://docs.expo.dev/versions/latest/.
Should I always create a new project when upgrading Expo SDK?
Not always, but it's a reliable fallback when you're stuck. Creating a new project with `npx create-expo-app@latest` and then migrating your source files (components, assets, config) can help isolate issues caused by stale lockfiles or corrupted native folders. It's especially recommended when upgrading across multiple SDK versions at once.
What does 'Unsupported SDK version' in Expo Go mean?
Expo Go only supports the latest stable SDK version. If you've upgraded your project to a newer SDK than what Expo Go supports, you'll see this error. The fix is to use a development build for testing (via `npx expo run:ios` or `npx expo run:android`) or wait for Expo Go to update. You can check the supported SDK version in Expo Go's app info.
How do I rollback an Expo SDK upgrade?
If you haven't committed changes, use git: `git checkout -- package.json app.json yarn.lock` and delete node_modules, then reinstall. If you have committed, revert the commit and reset your lockfile. Then run `npx expo install --fix` to restore the previous SDK's dependencies. For future upgrades, always work on a separate branch.