A local Docker build suddenly started failing with a MODULE_NOT_FOUND error, while the exact same build continued to pass in CI/CD.
Here’s how I tracked it down.
About the bug
Previously, I ran into a strange bug when building my Docker image locally:
node:internal/modules/cjs/loader:1424
throw err;
^
Error: Cannot find module 'apps/web/node_modules/next/dist/bin/next'
at Module._resolveFilename (node:internal/modules/cjs/loader:1421:15)
at defaultResolveImpl (node:internal/modules/cjs/loader:1059:19)
at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1064:22)
at Module._load (node:internal/modules/cjs/loader:1227:37)
at TracingChannel.traceSync (node:diagnostics_channel:328:14)
at wrapModuleLoad (node:internal/modules/cjs/loader:245:24)
at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5)
at node:internal/main/run_main_module:33:47 {
code: 'MODULE_NOT_FOUND',
requireStack: []
}
What made the issue particularly confusing was that the exact same Docker build had been working perfectly in CI/CD, while consistently failing on my local machine.
Before discussing the fix, here is some context about the project:
- It is a pnpm monorepo containing both frontend and backend workspaces.
-
pnpm devhad always worked locally. - The Docker build pipeline had always worked in CI/CD.
- The version of
nexthad never changed since the project was created.
During the debugging session, I noticed that I had pnpm 9.x.x upgraded to 10.x.x at some point.
That observation led me to test whether the problem could be reproduced outside Docker. Surprisingly, I got the same error:
➜ fileops git:(main) ✗ pnpm --filter web exec next build
`next` is not Found
Hmmm, interesting. As a final experiment, I ran
pnpm install
and it fixed the issue immediately.
But why? And... What happened?
Solution
Here's my most valuable takeaway:
When Docker builds work in CI/CD but fail locally, suspect local-only files entering the Docker build context.
In my case, CI worked because it built from a clean checkout. Local builds failed because stale workspace-level node_modules (under apps/web/) from a pnpm monorepo leaked into the Docker context. After upgrading pnpm from 9.x to 10.x, the local dependency layout was stale. pnpm dev still worked, so the issue stayed hidden until Docker ran a production build.
The fix was twofold:
1. Regenerate local dependencies:
pnpm install
Even though my NextJS version had not changed, pnpm's generated node_modules structure had become stale after upgrading pnpm. Pnpm uses a link structure for managing packages, and the algorithm for generating the link structure might change from one version to another.
If you'd like to know more, check out these links:
2. Make .dockerignore monorepo-safe:
- node_modules
+ **/node_modules
**/.next
My original .dockerignore only excluded the root-level node_modules directory. In a monorepo, workspace-level dependency directories can also exist, and excluding them helps prevent stale workspace dependencies and NextJS build artifacts from entering the Docker build context.
Looking back, pnpm install sounds almost unrelated to a Docker build failure. Have you run into bugs where the actual solution only made sense in hindsight?
Top comments (0)