Node.js / Editorial solution guide
Module not found and CommonJS–ESM mismatches
Separate path resolution, module format, and export-contract failures before changing package installation.
Read-only knowledge base · No public comments
Recognize the symptoms
Startup reports ERR_MODULE_NOT_FOUND, MODULE_NOT_FOUND, require is not defined, or a missing named export. A file may run from one folder but fail from another. These messages describe different stages: locating a target, interpreting its format, or matching its exports. Reinstalling dependencies cannot reliably repair all three.
Understand the likely causes
- Typical causes include a missing relative extension, incorrect filename case, an import resolved from the wrong module, a package export restriction, or require inside .mjs. The nearest package.json type field affects .js files; explicit .mjs and .cjs extensions remove that ambiguity. A missing installed package is only one possible cause.
Classify the exact message
Record the Node version, command, file extension, nearest package.json, and exact importing file. Inspect the requested specifier before editing anything. A relative specifier such as ./math.mjs is resolved from the importing module, while a bare package name uses package resolution and may be constrained by exports. Filesystem reads using relative paths follow a different working-directory rule. On case-sensitive filesystems, Math.mjs and math.mjs are different names. Check these facts in a read-only inspection before adding flags or changing every project file.
Repair the module format deliberately
In ESM, use import and export and keep relative file extensions. In CommonJS, use require and module.exports with an explicitly selected format. Do not rename a whole project merely to silence one error; libraries, test tools, and configuration files may need different formats. The non-runnable fragment demonstrates a format mismatch rather than a complete program. Its repair is not installing a registry package named node:path. Core modules already ship with Node, and the node: prefix makes that origin clear.
// broken.mjs: intentionally invalid in this module format
const { join } = require('node:path');
This intentional fragment uses CommonJS require in an ESM file and is marked non-runnable. Replace it with import { join } from node:path in ESM source.
Verify a real module boundary
The complete example creates two small source files in its own temporary directory. The consumer imports an exact named export through a relative .mjs specifier, and the parent loads that consumer with pathToFileURL. The expected result is 12. Temporary generation keeps every required file visible within one runnable demonstration; ordinary application modules normally live in source control. If a real package lacks an exported subpath, use its documented public entry point instead of reaching into internal files that a future release can move.
import { mkdtemp, writeFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { pathToFileURL } from 'node:url';
const dir = await mkdtemp(join(tmpdir(), 'esm-guide-'));
try {
await writeFile(join(dir, 'math.mjs'), 'export const triple = n => n * 3;');
const app = join(dir, 'app.mjs');
await writeFile(app, "import { triple } from './math.mjs'; export const result = triple(4);");
const { result } = await import(pathToFileURL(app).href);
console.log(result);
} finally { await rm(dir, { recursive: true, force: true }); }
12
The relative extension and named export agree. pathToFileURL safely represents the parent’s filesystem path as an import URL.
Change dependencies only when indicated
If the missing target really is a declared package, compare package.json and the lockfile, confirm the intended workspace, and use the project’s documented repeatable install. npm ci should not silently regenerate a mismatched lockfile. Review changes before adopting a newer major release because its public exports or supported runtime may differ. Do not delete every lockfile as a first diagnostic step: that replaces evidence and can produce a different dependency graph. Keep the fix focused on the actual resolution or format failure.
Verify the fix
Run the repaired entry point under the documented runtime, then run the relevant tests from both the project directory and an intentionally different working directory where appropriate. Confirm the error disappears without new warnings and that CI with a case-sensitive filesystem can load the same exports. The lab script must print 12 and exit naturally.
Keep it from recurring
- Choose explicit module formats, preserve exact relative extensions and filename case, document package entry points, and keep startup side effects outside reusable modules. Commit manifest and lockfile together. Test imports in CI instead of relying only on a developer machine’s path behavior.