Error [ERR_MODULE_NOT_FOUND]: relative import is missing its file extension
Node's native ESM resolver requires fully specified relative import paths, including the extension — `import './utils'` fails; it must be `import './utils.js'`. This trips people up constantly when migrating a CommonJS or bundler-based project to native ESM.
What causes this error
Bundlers like Webpack and Vite let you omit extensions and resolve index.js automatically. Node's native ESM loader does neither — it resolves relative specifiers literally, so any import missing an extension throws ERR_MODULE_NOT_FOUND at runtime, not at build time.
The Fix
// Wrong — works in a bundler, fails in native Node ESM
import { helper } from './utils';
// Fix — always include the extension
import { helper } from './utils.js';If you write TypeScript, this feels backwards: you import the compiled .js extension even though the source file on disk is .ts — that's expected, since imports resolve against the compiled output.
Common causes / variations
- Porting a bundler-based frontend project's import style directly into a Node backend
- Editor auto-import suggestions that omit the extension
- Barrel files (`index.ts`) imported as a directory without the trailing `/index.js`
Related errors
See also: ERR_REQUIRE_ESM, ERR_PACKAGE_PATH_NOT_EXPORTED.
Comments
Loading comments...