Node ERR_MODULE_NOT_FOUND - Fix ESM Import Resolution in CI
Under native ESM, Node does not guess file extensions the way CommonJS did. An import without an explicit extension (or to a non-existent path) fails with ERR_MODULE_NOT_FOUND.
What this error means
A type: module package (or .mjs) crashes resolving a relative import with ERR_MODULE_NOT_FOUND. It frequently appears after compiling TypeScript to ESM, where emitted imports lack the .js extension.
node:internal/errors ... [ERR_MODULE_NOT_FOUND]:
Cannot find module '/app/dist/utils' imported from /app/dist/index.js
Did you mean to import ./utils.js?Common causes
Missing file extension in ESM imports
Native ESM requires the full specifier, including the extension. import x from "./utils" fails; ./utils.js is required.
TypeScript emitted extensionless imports
TS source written ./utils and the compiler did not rewrite it to ./utils.js, so the emitted ESM cannot be resolved by Node.
Wrong path or missing index resolution
ESM does not auto-resolve directory index.js the way CJS does unless explicitly configured; a bare directory import fails.
How to fix it
Use explicit extensions in ESM
Add the file extension to relative imports.
// before
import { x } from './utils';
// after
import { x } from './utils.js';Align module settings
- Set tsconfig
module/moduleResolutionto a Node ESM mode (e.g. NodeNext). - Reference directory entry points explicitly (
./dir/index.js). - Verify dist output paths exist and match the import specifiers.
How to prevent it
- Use explicit
.jsextensions in ESM/TS-to-ESM imports. - Adopt NodeNext module resolution in tsconfig.
- Test the built ESM output in CI, not just the TS source.