Skip to content

Diagnosing a missing module npm install silently didn't provide

Confirms the module truly isn't installed, then checks the two most common install-time reasons: a platform-specific optionalDependency (native addons like esbuild/sharp binaries) skipped because the install environment's OS/architecture didn't match the runtime environment's, and a lockfile out of sync with package.json. It does not cover an ESM 'ERR_MODULE_NOT_FOUND' extension or exports-map error, which is a resolution-time problem with a package that IS installed, covered by a separate playbook.

Unverifiedno reproductions yetWhy this confidence?
Revision 1published by DevYou curationrevision history
Run the diagnosisEvidence and compatibility

Symptoms

The diagnostic path

8 steps. Every step is written out below in full — the interactive version simply follows the branches for you.

  1. Step 1 · StartCannot find module despite a clean npm install

    npm reporting success doesn't guarantee everything your code imports actually landed in node_modules — optional/platform-specific packages and lockfile drift are both silent by design in different ways.

    What happens next

    • passed step 2, Is the missing module actually present in node_modules?
  2. Step 2 · TestIs the missing module actually present in node_modules?

    Confirm the directory genuinely doesn't exist before assuming this is an install problem at all.

    Read-onlysh
    ls node_modules/<package-name>

    Expected result

    ls: cannot access 'node_modules/<package-name>': No such file or directory

    What happens next

    • passed step 3, Is it a platform-specific optional dependency (a native addon)?
    • failed step 4, The package is actually installed — this isn't a missing-install problem
    • unknown step 3, Is it a platform-specific optional dependency (a native addon)?
  3. Step 3 · TestIs it a platform-specific optional dependency (a native addon)?

    Packages like esbuild, sharp, or @swc/core ship per-platform binary packages under optionalDependencies, which npm silently skips installing when they don't match the current OS/CPU. That's by design, not a bug.

    Read-onlysh
    grep -A5 '"optionalDependencies"' package.json

    Expected result

    "optionalDependencies": {
      "@esbuild/linux-x64": "0.21.5",
      "@esbuild/darwin-arm64": "0.21.5"
    }

    What happens next

    • passed step 5, Root cause: install and runtime environments don't share an OS/architecture
    • failed step 6, Does package.json list the dependency, and does the lockfile agree?
    • unknown step 6, Does package.json list the dependency, and does the lockfile agree?
  4. Step 4 · EndThe package is actually installed — this isn't a missing-install problem

    Check the exact import path and casing instead (Linux is case-sensitive; a path that works on macOS/Windows can 404 on Linux CI), or a package.json 'exports' restriction blocking a deep import — that's covered by the ESM ERRMODULENOT_FOUND playbook, not this one.

  5. Step 5 · Root causeRoot cause: install and runtime environments don't share an OS/architecture

    If the environment that ran npm install (for example an earlier Docker build stage, or a CI cache) has a different OS or CPU architecture than the one actually running the app, the platform package for the runtime environment never gets installed. Run npm install (or npm ci) inside the actual target environment/architecture rather than copying a node_modules built elsewhere, or explicitly install the correct platform package there.

  6. Step 6 · TestDoes package.json list the dependency, and does the lockfile agree?

    Check both files for the dependency, and confirm the lockfile actually matches the package manager being used to install (an npm ci run against a yarn.lock, or vice versa, won't add anything).

    Read-onlysh
    grep '"<package-name>"' package.json package-lock.json

    Expected result

    package.json:  "<package-name>": "^2.1.0",

    What happens next

    • passed step 7, Fix: resync the lockfile with package.json
    • failed step 8, package.json and the lockfile already agree
    • unknown step 8, package.json and the lockfile already agree
  7. Step 7 · FixFix: resync the lockfile with package.json

    The dependency is listed in package.json but missing (or mismatched) in the lockfile — commonly because a lockfile update wasn't committed, a merge conflict dropped the entry, or the wrong package manager's lockfile is in use. Regenerate it, then commit the result.

    Changes statesh

    Changes system or service state. Review before running.

    Rewrites package-lock.json to match package.json (adding the missing entry) and installs into node_modules; commit the updated lockfile afterward so CI and other clones stay in sync.

    npm install
  8. Step 8 · Endpackage.json and the lockfile already agree

    This isn't a missing-entry problem — check whether the install step actually ran in the directory containing this package.json at all (a monorepo/workspace-root vs subpackage mismatch is a common way for npm install to report success while installing a different package.json's dependencies), or whether a project .npmrc points installs at a different registry that silently no-ops for a private package it can't reach.

Sources

Why this confidence?

What would strengthen it: 6 more independent reproductions. Reproductions from 3 more distinct environments.