Cannot find module
Why Node's require or import can't find a module that looks installed, the install-vs-path-vs-ESM causes, and how to fix each one.
Last updated
Error: Cannot find module 'express'
Require stack:
- /app/server.js
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1075:15)What it means: Node tried to require() or import a module by that name or path and couldn't locate it anywhere it looks — node_modules, the given relative path, or its own built-ins.
Likely causes
Most probable first — with how to confirm.
- 1.The package was never installed, or `node_modules` is missing or stale — check with `ls node_modules/<name>` (or `dir` on Windows), or just re-run the install.
- 2.A relative import path is wrong, or missing its file extension in an ESM project (`./utils` instead of `./utils.js`) — check the exact path against your module system, CommonJS or ESM.
- 3.You're running the script from a different working directory than expected, or in a monorepo the package lives in a different workspace than the one Node is resolving from.
- 4.The dependency was dropped from package.json or the lockfile during a manual edit or a bad merge — check package.json still lists it.
Fixes
Safest first; destructive ones are called out.
- →Run your package manager's install (`npm install`, `pnpm install`, or `yarn`) so `node_modules` matches package.json and the lockfile.
- →Double-check the import path and spelling — case matters on Linux and macOS filesystems even if it happened to work on a case-insensitive Windows setup.
- →In an ESM project (`"type": "module"` in package.json), add the file extension to relative imports — Node's ESM resolver doesn't infer it the way CommonJS does.
- →If the tree looks inconsistent, delete `node_modules` and run `npm ci`, which rebuilds it from the lockfile exactly. Do NOT delete the lockfile as part of a repair — that re-resolves every transitive version against the current registry, which is a dependency upgrade wearing a fix's clothing, and belongs in its own commit.
Part by part
- Cannot find module
- Node's resolver walked every node_modules directory from the file's location to the filesystem root and found nothing matching.
- 'express'
- What it looked for. A bare name means a package; a name starting with a dot means a relative file, and the two fail for entirely different reasons.
Common mistakes
npm install expressCorrect when the package is genuinely absent, and a misdiagnosis when the name is a relative path typo or the file exists with a different extension. Read the quoted name first — if it starts with a dot, installing anything is the wrong move.
rm -rf node_modules && npm installSometimes works and hides what happened. If the module is in package.json and missing from disk, npm ci is faster and tells you plainly that the lockfile and the tree disagreed.
npm install -g expressGlobal installs are not on the resolution path for a project's require or import. The package appears installed and stays invisible to your code, which is a confusing state to debug.
Questions
How do I tell a missing package from a bad path?
Look at the quoted name. A bare name such as 'express' is a package lookup; anything beginning with ./ or ../ is a file lookup, and that one is a typo, a wrong extension, or a case mismatch.
Why does it work on my machine but not in Docker or CI?
Case sensitivity, most often. macOS and Windows filesystems are case-insensitive by default and Linux is not, so requiring ./Utils when the file is utils.js works locally and fails everywhere else.
Why does it fail after switching branches?
The other branch added a dependency your node_modules does not have. npm ci brings the tree back in line with the lockfile, which is what switching branches should generally be followed by.
What about a TypeScript path alias that will not resolve?
tsconfig paths are compile-time only. Node knows nothing about them at runtime, so the alias needs a resolver — a bundler, tsx, or the imports field in package.json — or it will fail exactly like this.