After switching git branches, a React Native app fails with Metro's 'Unable to resolve module' error. How do you triage it?
answer
- read which module and from which file
- package: node_modules did not switch
- relative file: exists on this branch?
- stale file map: restart, then reset
- watchman watch-del-all as escalation
basics
~20 sRead which module failed and which file imported it. A missing package usually means node_modules still matches the old branch, so reinstall. A missing relative file is a real code change. If the file exists, restart Metro, then reset caches.
solid answer
~50 sStart from the message: Metro says `Unable to resolve module X from Y`, and either lists the file candidates it tried (`None of these files exist`) or says the package could not be found in the project or its `node_modules` folders. If X is a **package**, the usual cause is that `node_modules` did not change with the branch: git does not track it, so run your package manager's install. If X is a **relative path**, check whether the file exists on this branch and whether the importing file is itself stale. If the file is there and Metro still fails, Metro's **file map** is out of date: a dev server kept running through a large checkout, or a watcher that missed events. Restart Metro, then `npx react-native start --reset-cache` or `npx expo start --clear`, then `watchman watch-del-all`. Only after that suspect resolver configuration.
code
bash · 8 lines# 1. Branch changed dependencies? Reinstall (use the project's package manager)
npm install
# 2. File exists but Metro disagrees? Restart clean
npx react-native start --reset-cache # or: npx expo start --clear
# 3. Still stale? Reset Watchman's watches, then start again
watchman watch-del-allgo deeper
Read the error's module and importing file, reinstall dependencies after switching branches, and restart Metro before anything else.
Separate missing packages, missing relative files and a stale file map, and know which command fixes each.
Run the escalation in order, restart, cache reset, Watchman, reinstall, and only then compare resolver or Babel alias configuration between branches.
Reduce the whole class of errors with team habits: automatic installs on lockfile changes and a documented triage order instead of ad-hoc resets.
## The scenario You switch from `main` to a colleague's feature branch, reload the app, and the red screen says: ``` Unable to resolve module @scope/charts from /app/src/screens/Stats.tsx: @scope/charts could not be found within the project or in these directories: node_modules ``` or, for a relative import: ``` Unable to resolve module ./StatsCard from /app/src/screens/Stats.tsx: None of these files exist: * src/screens/StatsCard(.ios.js|.native.js|.js|...|.ios.tsx|.native.tsx|.tsx) * src/screens/StatsCard ``` The message tells you **what** could not be found and **who** asked for it. That is enough to pick the branch of the triage. ## Step 1: a package that cannot be found Git does not track `node_modules`. When the feature branch adds a dependency, its `package.json` and lockfile change, but the installed packages still belong to the previous branch. Metro reports the package as missing from `node_modules`, and it is right. 1. Run the project's install command (`npm install`, `yarn`, `pnpm install` or `bun install`). 2. If the dependency has **native code**, rebuild the app as well; resolving the JavaScript is not enough. 3. Restart Metro. This is the most common cause after a branch switch, and no cache reset fixes it. ## Step 2: a relative file that cannot be found 1. Check that the file exists at that path **on this branch**. Renames and moves are frequent in feature branches. 2. Check the importing file: if the other branch changed an import and this branch did not, the error is a genuine code problem, not a cache problem. 3. Watch for platform files: the candidate list shows Metro tried platform suffixes such as `.ios.tsx`, `.native.tsx` and plain `.tsx`. A file that exists only as `.android.tsx` fails when you bundle for iOS. ## Step 3: the file exists but Metro disagrees Now the cache is the suspect. Metro resolves against its **file map**, an in-memory index of project files, persisted to disk so restarts are fast. A running dev server updates it from file-system events, delivered by **Watchman** when installed and otherwise by Node's own watcher. A checkout that rewrites thousands of files at once can leave the map behind, especially if the watcher had to recrawl. Metro's Watchman integration can defer processing while a source-control operation runs, but its default deferred state, `hg.update`, is Mercurial's, so a git checkout gets no such pause by default. Escalate in this order and stop as soon as the error goes: 1. **Restart Metro.** A fresh start rebuilds the file map against the disk. 2. **Reset Metro's cache:** `npx react-native start --reset-cache` or `npx expo start --clear`. This discards both the transform cache and the persisted file map. 3. **Reset Watchman:** `watchman watch-del-all`, then start Metro again. 4. **Reinstall dependencies** from scratch if the error mentions a package inside `node_modules`. 5. As a last resort, delete Metro's temporary files: `rm -rf ${TMPDIR:-/tmp}/metro-*`. ## Step 4: only now suspect configuration If the error survives a clean start with the file present, the problem is resolution rules: an alias defined by a Babel plugin or `resolveRequest` that differs between branches, a `blockList` pattern hiding the folder, or a file outside `projectRoot` and `watchFolders`. Compare `metro.config.js` and `babel.config.js` between the two branches. A Babel alias change in a bare React Native app also needs a cache reset, because its Babel config is not part of the transform cache key. ## A quick decision table | Error detail | Most likely cause | First action | |---|---|---| | package not found in `node_modules` | dependencies from the old branch | install, then restart Metro | | relative file, file missing on disk | the branch renamed or removed it | fix the import or file | | relative file, file present | stale file map | restart, then reset cache | | survives a clean start | resolver or alias config | compare configs between branches | ## Habits that prevent it - Stop Metro before large checkouts, or restart it right after. - Run the install command after every branch switch that touches the lockfile; a git hook can do it automatically. - Resist resetting everything first: reading the message usually points straight at the cause and saves a slow cold start.
- Why does the error often mention a package even though the code on the branch is correct?The branch added a dependency to `package.json`, but `node_modules` is not tracked by git and still holds the previous branch's packages. Metro correctly reports the package as missing until you run the install command, and a native dependency also needs an app rebuild.
- What is Watchman's role in these errors?Watchman reports file-system changes to Metro so its file map stays current while the dev server runs. If its watch falls behind or is corrupted, Metro can believe a file is missing. Restarting Metro rebuilds the map; `watchman watch-del-all` clears Watchman's own state when that is not enough.
saying these in an interview costs you the question
- Always reset Metro's cache first, whatever the error says
- git checkout also switches the installed node_modules
- --reset-cache installs dependencies the new branch added
- If the file exists on disk, Metro must be able to resolve it
- Watchman pauses Metro automatically during every git checkout