jestjs/jest · error · ModuleNotFoundError
Cannot find module '${moduleName}' from '${relativePath}'
Error message
Cannot find module '${moduleName}' from '${relativePath}' What it means
ModuleNotFoundError thrown by Resolver._throwModNotFoundError after every resolution strategy (moduleNameMapper, cache, haste modules, node_modules via defaultResolver, haste packages) has been exhausted. The message names the missing module and the file that tried to import it, which is the canonical Jest 'cannot find module' diagnostic.
Source
Thrown at packages/jest-resolve/src/resolver.ts:461
}
/**
* _getHasteModulePath attempts to return the path to a haste module.
*/
private _getHasteModulePath(moduleName: string) {
const parts = moduleName.split('/');
const hastePackage = this.getPackage(parts.shift()!);
if (hastePackage) {
return path.join(path.dirname(hastePackage), ...parts);
}
return null;
}
private _throwModNotFoundError(from: string, moduleName: string): never {
const relativePath =
slash(path.relative(this._options.rootDir, from)) || '.';
throw new ModuleNotFoundError(
`Cannot find module '${moduleName}' from '${relativePath}'`,
moduleName,
);
}
private _getMapModuleName(matches: RegExpMatchArray | null) {
return matches
? (moduleName: string) =>
moduleName.replaceAll(
/\$(\d+)/g,
(_, index) => matches[Number.parseInt(index, 10)] || '',
)
: (moduleName: string) => moduleName;
}
private _isAliasModule(moduleName: string): boolean {
const moduleNameMapper = this._options.moduleNameMapper;
if (!moduleNameMapper) {View on GitHub (pinned to f49721c78e)
Solutions
- Run `npm ls <moduleName>` or `ls node_modules/<moduleName>` to confirm the package is installed at the version expected.
- Check `jest.config` `moduleNameMapper` — if a regex matches this import, verify the replacement path resolves from `rootDir`.
- Verify `moduleDirectories` (defaults to `['node_modules']`) and `roots` include the directories you expect Jest to search.
- On case-sensitive filesystems (Linux CI), correct the import casing to match the installed package name exactly.
- For workspace/monorepo packages, ensure the package is built (`main`/`exports` field points to an existing file) and linked via the workspace tool.
Example fix
// before — jest.config.js
module.exports = { moduleNameMapper: { '^@app/(.*)$': 'src/$1' } };
// import '@app/utils/foo' fails because 'src/utils/foo' doesn't exist
// after — map to the correct root
module.exports = { moduleNameMapper: { '^@app/(.*)$': '<rootDir>/src/$1' } }; Defensive patterns
Strategy: validation
Validate before calling
// In a setup file or pre-test script — confirm modules resolve
const resolver = require('jest-resolve');
function assertResolvable(fromDir, name, opts) {
const r = new resolver.Resolver(opts, {});
if (!r.resolveModule(fromDir, name)) {
throw new Error(`Pre-check: '${name}' does not resolve from ${fromDir}`);
}
} Try / catch
// Wrap a dynamic require you suspect may fail
try {
require('maybe-missing');
} catch (e) {
if (e.message.includes("Cannot find module")) {
// provide a fallback or fail with a clearer message
}
throw e;
} Prevention
- Run `npm ls <pkg>` in CI as a pre-test step for critical dependencies.
- Keep moduleNameMapper patterns specific (anchored) to avoid accidental greedy matches.
- Lint import paths with eslint-plugin-import to catch typos and missing extensions early.
- On case-sensitive CI, enforce case-correct imports via eslint import rules.
When it happens
Trigger: A test or source file calls `require('x')` / `import 'x'` where `x` is not installed, not on NODE_PATH, not matched by moduleNameMapper, and not registered as a haste module. Also thrown when a moduleNameMapper regex matches but the mapped target itself does not resolve (mapper matches are strict — no fallback).
Common situations: Forgot to `npm install` a dependency; typo in module name; moduleNameMapper regex too greedy and capturing the wrong path; `moduleDirectories`/`roots` misconfigured so node_modules isn't searched; importing a workspace package whose name differs from its directory; Node version mismatch where a builtin was renamed; case-sensitivity differences between macOS (case-insensitive) and Linux CI.
Related errors
- Unable to load resolver at ${options.resolver}
- Resolver located at ${resolver} does not export anything
- Resolver located at ${resolver} does not export a function o
- The --config option requires a JSON string literal, or a fil
- Cannot merge config in form of callback
AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03).
Data as JSON: /data/errors/c76fafca110a9642.json.
Report an issue: GitHub.