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

  1. Look at the console output immediately before the throw — it names both colliding files; delete or rename one.
  2. Add a platform extension (e.g. `Foo.ios.js`) if the files are intentionally platform-specific.
  3. Remove stale compiled output or build artifacts from the roots (`npm run clean`, clear `dist/`).
  4. If collisions are expected and non-fatal, set `throwOnModuleCollision: false` (the default) — collisions become warnings.
  5. 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

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


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)