jestjs/jest · error · DuplicateError
Duplicated files or mocks. Please check the console for…
Error message
Duplicated files or mocks. Please check the console for more info
What it means
Two different files resolve to the SAME haste module ID for the SAME platform (e.g. two `Foo.js` without platform extensions, or `Foo.js` and `Foo.ts` both yielding ID 'Foo'). When `throwOnModuleCollision` is enabled, FileProcessor.processFile's setModule() aborts the build by throwing a DuplicateError carrying both file paths. Without that flag it only warns and removes the ambiguous entry.
Solutions
- Look at the console output immediately before the throw — it names both colliding files; delete or rename one.
- Add a platform extension (e.g. `Foo.ios.js`) if the files are intentionally platform-specific.
- Remove stale compiled output or build artifacts from the roots (`npm run clean`, clear `dist/`).
- If collisions are expected and non-fatal, set `throwOnModuleCollision: false` (the default) — collisions become warnings.
- Configure a custom `hasteImplModulePath` to derive unique IDs if the default name extraction is too coarse.
Example fix
// before: both src/Foo.js and src/Foo.ts exist, both yield haste ID 'Foo' // after: remove the compiled JS or namespace it // rm src/Foo.js (keep Foo.ts) // or rename: src/Foo.legacy.js
Defensive patterns
Strategy: validation
Validate before calling
// Pre-scan for haste ID collisions before running Jest.
import glob from 'fast-glob';
import path from 'node:path';
async function findHasteCollisions(roots: string[], extensions: string[]) {
const byId = new Map<string, string[]>();
const pattern = roots.map(r => `${r}/**/*.{${extensions.join(',')}}`);
for (const file of await glob(pattern)) {
const id = path.basename(file).replace(/\.[^.]+$/, '');
if (!byId.has(id)) byId.set(id, []);
byId.get(id)!.push(file);
}
const collisions = [...byId.entries()].filter(([, files]) => files.length > 1);
if (collisions.length) console.warn('Haste collisions:', collisions);
return collisions;
} Try / catch
try {
await hasteMap.build();
} catch (error) {
if (error.constructor.name === 'DuplicateError') {
console.error(`Collision: ${error.mockPath1} vs ${error.mockPath2}`);
// decide: delete one, or disable throwOnModuleCollision
}
throw error;
} Prevention
- Run `jest --listTests` or a collision pre-scan in CI to catch duplicates early.
- Clean build output (dist/build) before running tests to avoid .js/.ts collisions.
- Use platform extensions or distinct filenames for intentional multi-platform modules.
- Set `throwOnModuleCollision: false` in watch mode (the library does this automatically).
When it happens
Trigger: In processFile, setModule() finds an existingModule for the same platform whose path differs from the new module (index.ts FileProcessor.ts:92). With `throwOnModuleCollision: true`, a DuplicateError is thrown at line 106. The console also prints both colliding paths before the throw.
Common situations: Adding a `.ts` duplicate of an existing `.js` file without platform disambiguation; two packages exporting the same haste name; stale build artifacts (`.js.map` vs `.js`) left in roots; switching a project to TypeScript while old compiled JS remains in the crawl path.
Related errors
- haste.enableSymlinks is incompatible with watchman
- jest.config.mts requires native TypeScript support. Ensure…
- Jest: Failed to parse the TypeScript config file
- jest-haste-map: the `ignorePattern` option must be a RegExp
- Jest: ' ' is not a valid TypeScript configuration loader.
AI-assisted analysis of jestjs/jest@8e6d128e4a (2026-08-10).
Data as JSON: /api/errors/58a6603753db9418.
Report an issue: GitHub.
Appendix: source
Thrown at packages/jest-haste-map/src/lib/FileProcessor.ts:106
H.GENERIC_PLATFORM;
const existingModule = moduleMap[platform];
if (existingModule && existingModule[H.PATH] !== module[H.PATH]) {
const method = this._options.throwOnModuleCollision ? 'error' : 'warn';
this._console[method](
[
`jest-haste-map: Haste module naming collision: ${id}`,
' The following files share their name; please adjust your hasteImpl:',
` * <rootDir>${path.sep}${existingModule[H.PATH]}`,
` * <rootDir>${path.sep}${module[H.PATH]}`,
'',
].join('\n'),
);
if (this._options.throwOnModuleCollision) {
throw new DuplicateError(existingModule[H.PATH], module[H.PATH]);
}
// We do NOT want consumers to use a module that is ambiguous.
delete moduleMap[platform];
if (Object.keys(moduleMap).length === 0) {
map.delete(id);
}
let dupsByPlatform = hasteMap.duplicates.get(id);
if (dupsByPlatform == null) {
dupsByPlatform = new Map();
hasteMap.duplicates.set(id, dupsByPlatform);
}
const dups = new Map([
[module[H.PATH], module[H.TYPE]],
[existingModule[H.PATH], existingModule[H.TYPE]],View on GitHub (pinned to 8e6d128e4a)