Node "ERR_MODULE_NOT_FOUND: Cannot find module" (missing .js extension) in CI
Native ES modules require the full file path including the extension on relative imports. import "./utils" fails because ESM does not do CommonJS-style extension guessing.
What this error means
An extensionless relative import like import x from "./utils" fails with "Error [ERR_MODULE_NOT_FOUND]: Cannot find module ... imported from", often suggesting the .js path.
node:internal/modules/esm/resolve:266
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/app/src/utils' imported from
/app/src/index.js
Did you mean to import "./utils.js"?Common causes
Extensionless relative import under native ESM
CommonJS resolves ./utils to utils.js; native ESM does not, so the bare specifier is not found.
TypeScript emitted import paths without .js
Code written ./utils and the compiler left the path as-is, but Node ESM needs the compiled .js extension.
How to fix it
Add the explicit file extension
Write the full path including .js (the emitted extension, even when the source is .ts).
import { parse } from './utils.js';
import config from './config/index.js';Let the compiler write extensions for you
With NodeNext resolution, TypeScript requires and preserves .js extensions in import specifiers.
// tsconfig.json
{
"compilerOptions": { "moduleResolution": "NodeNext", "module": "NodeNext" }
}How to prevent it
- Always write the
.jsextension on relative ESM imports. - Use NodeNext module resolution so the compiler enforces extensions.
- Do not rely on CommonJS index/extension resolution in ESM.