Node.js "Cannot find module" — Path, Deps, or Case Sensitivity

Three different problems share one message: a missing dependency, a relative path typo, or a case-sensitivity mismatch that works on macOS/Windows and fails on Linux. Match the fix to your case.

What you'll see

Root causes

Dependency not installed (or installed for the wrong context)

node_modules missing after clone, or the package is a devDependency used in production builds. npm ls <pkg> shows whether it's resolvable from your file.

Relative path typo — missing ./ or ../

require('config') looks in node_modules, not the local file — it must be require('./config'). The error's 'Require stack' points to the exact file that got it wrong.

Case-sensitivity mismatch

require('./utils/Cool') vs utils/cool.js: macOS/Windows match case-insensitively; Linux in CI/production doesn't. Every 'works locally, breaks in CI' case-mismatch smells like this.

Fix it

  1. Read the require stack — the first file in it made the bad call
    # full error output names the exact file+line; open it and verify the specifier
  2. For bare specifiers: install/verify the dep
    npm ls <pkg> ; npm install <pkg>   # or: npm ci from a clean node_modules
  3. For local files: fix the path shape and case
    grep -n "require(\"./\|require('./" <file> | head   # every local import starts with ./ or ../ — match the exact filename case
  4. For built-ins: drop any accidental paths
    require('fs')   # NOT require('node:fs.js') or ./fs — bare built-in names, optionally node: prefix

Field note

ESM import behaves the same for paths but requires file extensions for local files (import './x.js'). pnpm/yarn strictness differences: a phantom dependency (undeclared but resolvable via hoisting) fails exactly like this when you switch package managers.

Common questions

Why does it work on my Mac but fail in the Linux container?

Case sensitivity: macOS/Windows resolve filenames case-insensitively, Linux doesn't. Compare the require specifier's case against the real filename character by character — ./utils/Help vs utils/help.js.

npm ls says the package exists but Node can't find it.

Check import context: a require from a file inside node_modules resolves differently; a package.json 'type' mismatch (ESM/CJS) changes rules; and NODE_PATH hacks break in strict ESM. Simplify to a plain dependency declared in package.json.

Ship it right the first time

Our most-documented failures, packaged as ready-to-ship starter kits: Docker, Kubernetes, and Terraform.

Browse the template store →

One-time. Yours to modify. Instant download from the NinjaOps template store.