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.
| Error | Error [ERR_MODULE_NOT_FOUND]: Cannot find package ' |
|---|---|
| Where it happens | Node.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 means | Node'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
Jump to your case
- 1. The package is not installed at all
- 2. Installed in the wrong directory (monorepo / nested project)
- 3. Lockfile and node_modules are out of sync
- 4. Importing an ESM-only package from a CJS context without full specifier
- 5. Missing or incorrect exports map in the target package
- 6. Self-referencing a package without the exports field
- 7. Symlink points to a deleted or moved location
- 8. NODE_PATH or --require override hides the real node_modules
- 9. Package name has a typo or wrong scope
- 10. TypeScript path aliases not compiled or misconfigured
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.
- 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, runls -lato check if the target exists → cause 7. - Check you are running from the right directory. Run
pwdand compare to the location of yourpackage.json. If they differ, thenode_modulestree may not be reachable → cause 2. - Check the exact package name. Run
npm info <package-name>— if npm returns 404, you have a typo or wrong scope → cause 9. - 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. - Check if the package is ESM-only. Open
node_modules/<package-name>/package.jsonand look for"type": "module". If your importing file is CJS (.cjsextension or no"type": "module"in your own package.json) → cause 4. - Check for NODE_PATH usage. Run
echo $NODE_PATH. If set and you are using ESM (importsyntax) → cause 8. - 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. - Check self-referencing. If the import uses your own package's name, verify that your
package.jsonhas anexportsfield → 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.
Stop It From Coming Back
- Use
npm ciin CI and fresh environments instead ofnpm install. It installs from the lockfile exactly, catches mismatches immediately, and ensuresnode_modulesmatches what was tested locally. - Enable the
import/no-unresolvedESLint rule (fromeslint-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.jsonfor"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 lsafter dependency changes to verify the tree is consistent. It reports missing, extraneous, and invalid dependencies in one pass. - Avoid
NODE_PATHin ESM projects. Node explicitly ignoresNODE_PATHfor 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 itERR_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 mapMODULE_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.
댓글
댓글 쓰기