Skip to content

Fix: React Native Metro Bundler Failed to Start or Bundle

FixDevs ·

Quick Answer

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.

The Error

When running npx react-native start or npx expo start, Metro fails with one of these errors:

error: Error: Unable to resolve module `./src/screens/Home` from `App.js`

Or Metro fails to start:

Error: EMFILE: too many open files, watch

Or a port conflict:

Another process is already using port 8081. Run the following command to find out which process:
  lsof -i :8081

Or a transform error:

error: bundling failed: Error: Unable to resolve module `crypto` from `node_modules/some-library/index.js`

Or after an npm install, bundles fail silently and the simulator shows a red error screen with “Unable to load script.”

Why This Happens

Metro is React Native’s JavaScript bundler — it resolves module imports, transforms code, and serves the bundle to the simulator or device. Metro bundler failures stem from several common sources:

  • Stale cache — Metro caches transform results. After dependency changes, the cache can reference modules that have moved or been removed.
  • Module resolution failure — the moduleNameMapper or Metro config does not know how to resolve a path alias, a native module, or a Node.js built-in.
  • File watcher limits — on Linux, the default inotify limit is too low for large React Native projects.
  • Port conflict — Metro defaults to port 8081. Another Metro instance or a different process already occupies it.
  • Node.js built-in modules in web-targeted libraries — some npm packages use Node.js built-ins (crypto, stream, fs) that do not exist in React Native’s runtime.
  • Corrupted node_modules — incomplete installs or version conflicts cause Metro to fail resolving modules it expects to find.

Fix 1: Clear the Metro Cache

The most common fix for sudden Metro failures after npm install or git pull:

# Clear Metro's transform cache
npx react-native start --reset-cache

# Or for Expo
npx expo start --clear

# Full reset — clears watchman, metro cache, and node_modules cache
watchman watch-del-all
rm -rf $TMPDIR/metro-*
rm -rf $TMPDIR/react-*

For a complete clean start:

# Stop Metro if running
# Clear all caches
watchman watch-del-all 2>/dev/null || true
rm -rf node_modules
rm -rf $TMPDIR/metro-*
rm -rf $TMPDIR/haste-map-*
npm install
npx react-native start --reset-cache

Pro Tip: Add a clean script to your package.json for faster troubleshooting:

{
  "scripts": {
    "clean": "watchman watch-del-all && rm -rf $TMPDIR/metro-* && rm -rf node_modules && npm install",
    "start:clean": "npm run clean && npx react-native start --reset-cache"
  }
}

Fix 2: Fix Unable to Resolve Module

When Metro cannot find a module you are importing:

Check the import path first:

// Wrong — relative path typo
import HomeScreen from './screens/home'; // file is HomeScreen.js, not home.js

// Correct
import HomeScreen from './screens/HomeScreen';

Metro is case-sensitive even on macOS (unlike the filesystem). A file named HomeScreen.js is not resolved by ./screens/homescreen.

Add path aliases via Metro config:

// metro.config.js
const { getDefaultConfig } = require('@react-native/metro-config');
const path = require('path');

const config = getDefaultConfig(__dirname);

config.resolver.alias = {
  '@components': path.resolve(__dirname, 'src/components'),
  '@screens': path.resolve(__dirname, 'src/screens'),
  '@utils': path.resolve(__dirname, 'src/utils'),
};

module.exports = config;

Verify the module exists in node_modules:

ls node_modules/some-library/
# If empty or missing, reinstall:
npm install some-library

Fix missing peer dependencies:

npx react-native doctor
# Or
npx expo-doctor

Fix 3: Fix Node.js Built-in Modules Not Found

Some npm packages use Node.js built-ins (crypto, stream, buffer, path) that do not exist in React Native’s JavaScript environment:

error: bundling failed: Error: Unable to resolve module `crypto`

Install polyfill packages:

npm install react-native-crypto react-native-randombytes
npm install stream-browserify readable-stream
npm install react-native-buffer

Configure Metro to use polyfills:

// metro.config.js
const { getDefaultConfig } = require('@react-native/metro-config');

const config = getDefaultConfig(__dirname);

config.resolver.extraNodeModules = {
  crypto: require.resolve('react-native-crypto'),
  stream: require.resolve('stream-browserify'),
  buffer: require.resolve('react-native-buffer'),
  path: require.resolve('path-browserify'),
  os: require.resolve('react-native-os'),
};

module.exports = config;

Alternative — find a React Native compatible library:

Instead of polyfilling crypto, use a React Native native module:

# Use react-native-quick-crypto instead of the crypto polyfill
npm install react-native-quick-crypto
npx pod-install  # iOS

Fix 4: Fix EMFILE Too Many Open Files

On Linux and macOS, the file watcher limit is often too low for React Native projects with thousands of files:

Increase the inotify limit on Linux:

# Check the current limit
cat /proc/sys/fs/inotify/max_user_watches

# Increase it temporarily
sudo sysctl fs.inotify.max_user_watches=524288

# Make it permanent
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

On macOS — increase file descriptor limit:

# Check current limit
ulimit -n

# Increase for current session
ulimit -n 65536

# For permanent fix, add to ~/.zshrc or ~/.bash_profile:
echo "ulimit -n 65536" >> ~/.zshrc

Install and use Watchman (recommended):

Metro uses Watchman for file watching on macOS when available — it is more efficient than the native file watcher:

brew install watchman
watchman --version

# Reset Watchman state if it was previously broken
watchman watch-del-all

Fix 5: Fix Port 8081 Already in Use

Find and kill the process using port 8081:

# Find the process
lsof -i :8081

# Kill it
kill -9 $(lsof -t -i:8081)

# On Windows
netstat -ano | findstr :8081
taskkill /PID <PID> /F

Or run Metro on a different port:

npx react-native start --port 8082

# Then in a new terminal, run the app with the custom port:
npx react-native run-ios --port 8082
npx react-native run-android --port 8082

# For Expo:
npx expo start --port 8082

Update the app to use the new port (Android):

If you change the Metro port, Android needs to know:

adb reverse tcp:8082 tcp:8082

Fix 6: Fix Metro Transform and Babel Errors

Error: SyntaxError or Transform failed:

These usually indicate a Babel configuration issue or a file with syntax Metro cannot parse:

# Check your Babel config
cat babel.config.js
// babel.config.js — standard React Native config
module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: [
    // Optional: support path aliases (needs babel-plugin-module-resolver)
    [
      'module-resolver',
      {
        root: ['./src'],
        extensions: ['.ios.js', '.android.js', '.js', '.ts', '.tsx', '.json'],
        alias: {
          '@components': './src/components',
          '@screens': './src/screens',
        },
      },
    ],
  ],
};

Fix experimentalDecorators or TypeScript syntax errors:

# Install TypeScript support
npm install --save-dev @babel/plugin-proposal-decorators babel-plugin-parameter-decorator
// babel.config.js
module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: [
    ['@babel/plugin-proposal-decorators', { legacy: true }],
  ],
};

Clear the Babel cache after changing babel.config.js:

npx react-native start --reset-cache

Fix 7: Fix Metro on a Fresh Setup or CI Environment

When setting up a new environment or running in CI:

# Complete setup sequence
git clone https://github.com/your-org/your-app.git
cd your-app

# Install JS dependencies
npm ci

# iOS — install CocoaPods dependencies
cd ios && pod install && cd ..

# Start Metro (in one terminal)
npx react-native start

# Run on device (in another terminal)
npx react-native run-ios
npx react-native run-android

Common CI issues:

# CI may not have Watchman — disable it in Metro config
// metro.config.js
config.watchman = false; // Fall back to native file watching in CI

Android — ensure ADB is set up:

# Check connected devices
adb devices

# If no devices found for a running emulator:
adb kill-server && adb start-server

iOS — fix code signing for simulators:

# Simulators do not need code signing — check scheme settings
open ios/YourApp.xcworkspace
# Product → Scheme → Edit Scheme → Run → Build Configuration → Debug

Still Not Working?

Check the Metro output for the root error. Metro often shows a long stack trace — scroll to the top to find the original error message, which is more useful than the bottom of the stack.

Verify your Node.js version. React Native has specific Node.js version requirements. Check the project’s .nvmrc or the React Native version’s release notes:

node --version
# Use nvm to switch versions
nvm use 20
nvm install 20

Check for symlink issues. If you use npm link or workspaces, Metro may fail to resolve symlinked packages:

// metro.config.js — allow following symlinks
config.resolver.unstable_enableSymlinks = true;

Verify Xcode Command Line Tools on macOS:

xcode-select --install
sudo xcode-select --switch /Applications/Xcode.app

Check the device logs for additional error context:

# iOS simulator logs
xcrun simctl spawn booted log stream --level debug | grep -i "react native"

# Android device logs
adb logcat *:E | grep -i "react\|metro\|javascript"

For related React and JavaScript bundling issues, see Fix: webpack Module Not Found and Fix: Cannot Use Import Statement Outside a Module.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles