Skip to content
Latchkey

pnpm "Cannot find module" from strict hoisting (node-linker) in CI

pnpm builds an isolated, symlinked node_modules where a package can only import what it declares. Code that relied on a phantom dependency (something hoisted by npm/Yarn but never declared) fails with "Cannot find module" under pnpm.

What this error means

A build or test step throws "Error: Cannot find module 'X'" for a package that installs fine, and it works under npm or Yarn classic but not pnpm.

node
Error: Cannot find module 'lodash'
Require stack:
- /home/runner/work/app/app/packages/api/src/index.js
    at Function._resolveFilename (node:internal/modules/cjs/loader)

Common causes

A phantom (undeclared) dependency

The code imports a package that was never listed in its package.json; npm hoisting made it resolvable, but pnpm's isolated layout does not.

A package expects a flat node_modules

Some tools resolve peers or plugins by walking a flat tree; pnpm's symlinked structure exposes the missing declaration.

How to fix it

Declare the dependency explicitly

  1. Read the require stack to see which package imports the missing module.
  2. Add the module to that package's dependencies.
  3. Re-run pnpm install so the declared dependency is linked.
Terminal
pnpm --filter @acme/api add lodash

Adjust node-linker or hoisting only if a tool truly needs it

For a tool that cannot work with an isolated layout, switch to a hoisted or flat node_modules rather than papering over undeclared deps.

.npmrc
# pnpm-workspace.yaml (pnpm 9+) or .npmrc
node-linker=hoisted

How to prevent it

  • Declare every module you import in the package that imports it.
  • Keep node-linker=isolated (the default) to catch phantom deps early.
  • Use public-hoist-pattern sparingly and only for known-incompatible tools.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →