Error: ERR_MODULE_NOT_FOUND: Cannot find package - 10 Causes and Fixes

Error: ERR_MODULE_NOT_FOUND: Cannot find package fires when Node.js resolves an import or require() and the target package does not exist on any search path. The runtime tried every directory in the module resolution chain and came up empty.

This page covers 10 distinct causes — from a missing npm install to subtle ESM resolution rules — with a reproduction and fix for each. A diagnostic checklist at the end helps you narrow down cases that don't fit a single cause cleanly.

If you are in a hurry, the TL;DR fix below handles the majority of occurrences in one command. Close-up of JavaScript code on a laptop screen, showcasing programming in progress.

ErrorError [ERR_MODULE_NOT_FOUND]: Cannot find package '' imported from /path/to/your/file.mjs
Where it happensNode.js (v12.17+ with ES modules, or v14+ with --experimental-specifier-resolution). Appears in any framework that runs on Node: Next.js, Nuxt, Remix, plain scripts.
What it meansNode's module resolver walked every candidate directory for the package you imported and found nothing — the package is either not installed, not reachable from the importing file's location, or its export map does not expose the subpath you requested.

The Fast Fix

In most cases the package simply is not installed. Run install from the project root (the directory containing your package.json):

npm install <package-name>

If you are using a monorepo or workspaces, make sure you install from the workspace root or use the --workspace flag:

npm install <package-name> --workspace=packages/my-app

After installing, re-run your script. If the error persists, the cause is not a missing install — work through the diagnostic checklist below.

What Is Actually Causing It

1. The package is not installed at all

Reproduce it

# package.json has no "lodash" in dependencies
node -e "import 'lodash'"
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'lodash' imported from /home/user/app/index.mjs

Why it happens — Node searches node_modules directories up from the importing file. If the package was never installed — or was removed by a npm prune or stale node_modules — there is nothing to find.

The fix

npm install lodash

Add the dependency explicitly so it survives future installs.

Confirm it worked — Run node -e "import 'lodash'; console.log('ok')" — it should print ok with no error.


2. Installed in the wrong directory (monorepo / nested project)

Reproduce it

repo/
  packages/
    api/          ← you ran `npm install` here
    web/
      index.mjs   ← imports 'cors', which is only in api/node_modules
cd repo/packages/web && node index.mjs
# Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'cors'

Why it happens — Node walks up from the importing file looking for node_modules/cors. It finds web/node_modules (empty or missing cors), then packages/node_modules, then repo/node_modules. It never looks sideways into api/node_modules.

The fix

Install the dependency where the consuming package can reach it:

# Option A: install in the workspace that needs it
cd repo/packages/web && npm install cors

# Option B: hoist via workspaces (preferred)
# In repo/package.json:
# { "workspaces": ["packages/*"] }
npm install cors --workspace=packages/web

Confirm it worked — Run node packages/web/index.mjs from the repo root — the error should be gone.


3. Lockfile and node_modules are out of sync

Reproduce it

# Someone added a dependency to package.json but did not commit the lockfile,
# or you pulled and ran `npm install` with --ignore-scripts which skipped postinstall
git pull
node src/index.mjs
# Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'zod'

Why it happens — The package-lock.json (or yarn.lock / pnpm-lock.yaml) pins exact versions. When the lockfile disagrees with package.json or node_modules was partially written, the resolver can miss packages that are declared but not physically present.

The fix

Delete node_modules and reinstall from the lockfile:

rm -rf node_modules
npm ci

npm ci installs from the lockfile exactly, failing fast on mismatches rather than silently diverging.

Confirm it worked — Run ls node_modules/zod — the directory should exist. Then run your entry point again.


4. Importing an ESM-only package from a CJS context without full specifier

Reproduce it

// index.cjs (CommonJS file)
const chalk = require('chalk');
// Works fine with chalk 4.x
// But chalk 5.x is ESM-only:
// Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'chalk'
// (or ERR_REQUIRE_ESM depending on Node version)

Why it happens — Some packages (chalk 5, node-fetch 3, etc.) dropped CommonJS support. When you require() an ESM-only package, Node's CJS loader cannot resolve it through the ESM export map, and in some configurations this surfaces as ERR_MODULE_NOT_FOUND rather than the more specific ERR_REQUIRE_ESM.

The fix

Either switch your file to ESM or pin the last CJS-compatible version:

# Option A: pin the CJS version
npm install chalk@4

# Option B: switch to ESM
# Rename index.cjs → index.mjs (or set "type": "module" in package.json)
# Then:
import chalk from 'chalk';

Confirm it worked — Run the file — chalk('hello') should execute without any module resolution error.


5. Missing or incorrect exports map in the target package

Reproduce it

// Importing a subpath that the package does not expose
import { helper } from 'some-lib/utils';
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'some-lib/utils'

Why it happens — Since Node 12.7, packages can define an exports map in package.json that controls which subpaths are importable. If some-lib does not list "./utils" in its exports field, Node treats it as nonexistent — even if the file is physically on disk.

The fix

Check the package's package.json for its exports field and use a path it actually exposes:

// If the package exports "./utilities" instead of "./utils":
import { helper } from 'some-lib/utilities';

If you own the package, add the missing subpath:

{
  "exports": {
    ".": "./src/index.js",
    "./utils": "./src/utils.js"
  }
}

Confirm it worked — Run node -e "import 'some-lib/utils'" — it should resolve without error.


6. Self-referencing a package without the exports field

Reproduce it

// packages/my-lib/package.json
{ "name": "my-lib", "type": "module" }
// packages/my-lib/src/internal.mjs
import { version } from 'my-lib'; // self-reference
// Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'my-lib'

Why it happens — Node allows a package to import itself by its own name (self-referencing), but only when the package.json has an exports field. Without exports, Node falls back to the normal node_modules search and does not find the package in its own directory.

The fix

Add an exports field to your package.json:

{
  "name": "my-lib",
  "type": "module",
  "exports": {
    ".": "./src/index.mjs"
  }
}

Now import { version } from 'my-lib' resolves to ./src/index.mjs within the same package.

Confirm it worked — Run node packages/my-lib/src/internal.mjs — the self-reference import should succeed.


More causes (4 remaining)

7. Symlink points to a deleted or moved location

Reproduce it

# A previous `npm link` created a symlink, then the linked package was moved
ls -la node_modules/my-shared-lib
# my-shared-lib -> /home/user/old-path/my-shared-lib  (dead symlink)

node src/index.mjs
# Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'my-shared-lib'

Why it happens — Node follows the symlink, lands on a path that no longer exists, and reports the package as not found. npm link and pnpm both use symlinks, so stale links accumulate over time.

The fix

Remove the dead link and re-establish it:

rm node_modules/my-shared-lib
npm link /home/user/new-path/my-shared-lib

Or reinstall cleanly if you no longer need the link:

npm unlink my-shared-lib
npm install my-shared-lib

Confirm it worked — Run ls -la node_modules/my-shared-lib — the symlink target should exist. Then run your script.


8. NODE_PATH or --require override hides the real node_modules

Reproduce it

export NODE_PATH=/opt/global-node-libs
node --experimental-specifier-resolution=node src/index.mjs
# Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'express'

Why it happens — When NODE_PATH is set, Node adds those directories to the search list but only for CJS. In ESM mode, NODE_PATH is ignored entirely. If your setup depends on NODE_PATH to find packages and you switch to ESM, the resolution silently stops working.

The fix

Remove the NODE_PATH dependency and install packages locally:

unset NODE_PATH
npm install express

If you need shared packages across projects, use npm workspaces or a monorepo tool instead of NODE_PATH.

Confirm it worked — Run echo $NODE_PATH (should be empty) and then node src/index.mjs — express should resolve from the local node_modules.


9. Package name has a typo or wrong scope

Reproduce it

import Redis from '@redis/client';
// Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@redis/client'
// The actual package name is 'redis' (which internally uses @redis/client)

Why it happens — Scoped packages (@scope/name) and unscoped packages are entirely different identifiers. A single wrong character — missing scope, wrong scope, or misspelled name — sends Node down a search path that does not exist.

The fix

Check the exact published name on npm:

npm info redis | head -5

Then install and import the correct name:

npm install redis
import { createClient } from 'redis';

Confirm it worked — Run npm ls redis — it should appear in the tree. Then run your file.


10. TypeScript path aliases not compiled or misconfigured

Reproduce it

// tsconfig.json
{
  "compilerOptions": {
    "paths": { "@app/*": ["./src/*"] }
  }
}
// src/index.ts
import { db } from '@app/db';
tsc && node dist/index.mjs
# Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@app/db'

Why it happens — TypeScript's paths are a compile-time feature. tsc does not rewrite import specifiers in the emitted JavaScript. The compiled output still says '@app/db', and Node has no idea what @app means.

The fix

Use a path-rewriting tool at build time or runtime:

# Option A: use tsc-alias to rewrite paths after compilation
npm install -D tsc-alias
tsc && tsc-alias

# Option B: use tsx or ts-node with tsconfig-paths
npm install -D tsconfig-paths
node -r tsconfig-paths/register dist/index.js

Or configure your bundler (esbuild, webpack, vite) to resolve paths — they all support it natively.

Confirm it worked — Inspect dist/index.mjs — the import should now read './db.js' (or the resolved path), not '@app/db'. Run the file to confirm.

Close-up of AI-assisted coding with menu options for debugging and problem-solving. ## None of Those? Narrow It Down
  1. Check if the package exists in node_modules. Run ls node_modules/<package-name>. If missing → cause 1 (not installed) or cause 3 (out of sync). If it is a symlink, run ls -la to check if the target exists → cause 7.
  2. Check you are running from the right directory. Run pwd and compare to the location of your package.json. If they differ, the node_modules tree may not be reachable → cause 2.
  3. Check the exact package name. Run npm info <package-name> — if npm returns 404, you have a typo or wrong scope → cause 9.
  4. Check for an exports map conflict. Run node -e "console.log(require('<package-name>/package.json').exports)". If the field exists and your subpath is not listed → cause 5.
  5. Check if the package is ESM-only. Open node_modules/<package-name>/package.json and look for "type": "module". If your importing file is CJS (.cjs extension or no "type": "module" in your own package.json) → cause 4.
  6. Check for NODE_PATH usage. Run echo $NODE_PATH. If set and you are using ESM (import syntax) → cause 8.
  7. Check TypeScript path aliases. If the package name starts with @app/, @/, ~, or any non-npm prefix, it is likely a path alias that Node cannot resolve at runtime → cause 10.
  8. Check self-referencing. If the import uses your own package's name, verify that your package.json has an exports field → cause 6.

Why This Error Exists At All

Node.js resolves modules through a deterministic directory-walking algorithm. When you write import 'foo', Node starts at the directory of the importing file, looks for node_modules/foo, then walks up one directory and looks again, repeating until it hits the filesystem root. If none of those directories contain foo, resolution fails with ERR_MODULE_NOT_FOUND.

This design is intentional. Unlike Python's flat sys.path list or Java's classpath, Node ties resolution to filesystem locality. Each project — and each package inside a monorepo — can have its own version of a dependency without conflicts. The trade-off is that a package must be physically present in the right node_modules tree; being installed "somewhere on the machine" is not enough.

The introduction of ES modules in Node 12+ added a second layer: the exports field in package.json. This field acts as a gatekeeper — even if a file physically exists inside a package, it is not importable unless the exports map exposes it. This was a deliberate encapsulation decision: package authors can refactor internals without breaking consumers, because consumers can only reach the public API. The cost is that ERR_MODULE_NOT_FOUND now covers two distinct failure modes: the package is genuinely absent, or it is present but the requested entry point is not exported. The error message usually tells you which — read the full text after the colon carefully. Close-up of two red lever arch files on a wooden desk in a modern office setting.

Stop It From Coming Back

  • Use npm ci in CI and fresh environments instead of npm install. It installs from the lockfile exactly, catches mismatches immediately, and ensures node_modules matches what was tested locally.
  • Enable the import/no-unresolved ESLint rule (from eslint-plugin-import). It statically checks that every import target resolves, catching typos and missing packages at lint time rather than runtime.
  • Pin ESM-only migrations. Before upgrading a dependency to a major version, check its package.json for "type": "module". If your project is CJS, either stay on the last CJS-compatible version or plan the migration to ESM first.
  • Use TypeScript moduleResolution: "bundler" or "node16" instead of "node". The newer modes enforce that import specifiers match what Node actually resolves at runtime, catching path-alias and extension issues at compile time.
  • Run npm ls after dependency changes to verify the tree is consistent. It reports missing, extraneous, and invalid dependencies in one pass.
  • Avoid NODE_PATH in ESM projects. Node explicitly ignores NODE_PATH for ES module resolution. Replace it with npm workspaces or local installs.

Related Guides

Errors You Will Probably Hit Next

  • ERR_REQUIRE_ESM: require() of ES Module not supported — fires when you require() an ESM-only package instead of importing it
  • ERR_PACKAGE_PATH_NOT_EXPORTED: Package subpath './foo' is not defined by "exports" — fires when the package exists but the subpath is not in its exports map
  • MODULE_NOT_FOUND (without ERR_ prefix): Cannot find module './relative-path' — the CJS-era equivalent, usually for relative paths rather than packages

When you see ERR_MODULE_NOT_FOUND, split the problem in two: is the package missing, or is the entry point missing? ls node_modules/<name> answers the first question in one second. If the directory exists, the issue is almost always an exports map conflict, an ESM/CJS mismatch, or a path alias that Node cannot resolve at runtime. Train yourself to read the full error message past the colon — the file path it prints tells you exactly where resolution started and what it was looking for.

댓글

이 블로그의 인기 게시물

TypeError: Cannot read properties of undefined (reading 'map') - 11 Causes and Fixes

npm ERR! code ERESOLVE unable to resolve dependency tree: 9 Causes and Fixes