jestjs/jest · error · Error

Whoops! Two projects resolved to the same config path

Error message

Whoops! Two projects resolved to the same config path: ${chalk.bold(String(configPath))}:

  Project 1: ${chalk.bold(projects[parsedConfigs.indexOf(config)])}
  Project 2: ${chalk.bold(projects[parsedConfigs.indexOf(configPathMap.get(configPath))])}

This usually means that your ${chalk.bold('"projects"')} config includes a directory that doesn't have any configuration recognizable by Jest. Please fix it.

What it means

ensureNoDuplicateConfigs (index.ts:268-302) walks each parsed project in a multi-project setup and records its resolved configPath; if two projects map to the same path, it throws. This typically happens when a projects entry is a directory with no Jest-recognizable config, so Jest falls back to resolving the SAME parent config for both — collapsing what you intended as distinct projects into one.

Solutions

  1. Ensure each project directory listed in projects has its own jest.config.{js,ts,...} or a package.json with a jest key.
  2. Give each project a unique displayName so they are distinguishable.
  3. Check the two project paths reported in the message — the duplicate usually means one of them has no config and is inheriting the root.
  4. If a directory should share the root config intentionally, remove it from the projects array (the root already covers it).

Example fix

// before (packages/b has no jest.config)
module.exports = { projects: ['packages/a', 'packages/b'] };
// after
// add packages/b/jest.config.js with at least module.exports = { displayName: 'b' };
Defensive patterns

Strategy: validation

Validate before calling

import * as fs from 'node:fs';
import * as path from 'node:path';
const EXTS = ['.js','.ts','.mjs','.cjs','.json','.mts','.cts'];
function assertEachProjectHasConfig(projects: string[]): void {
  for (const dir of projects) {
    const hasCfg = fs.existsSync(path.join(dir, 'package.json')) && require(path.join(dir,'package.json')).jest
      || EXTS.some(e => fs.existsSync(path.join(dir, `jest.config${e}`)));
    if (!hasCfg) throw new Error(`Project ${dir} has no jest config; add one or remove from projects`);
  }
}

Prevention

When it happens

Trigger: projects: ['packages/a', 'packages/b'] where one directory lacks its own jest.config.*; two project paths that both resolve up to the root config; a glob in projects that yielded overlapping directories.

Common situations: Monorepo where a new package was added without a jest.config; projects pointing at empty/non-config directories; naming two project paths identically after normalization; mixing absolute and relative paths that resolve to the same location.

Related errors


AI-assisted analysis of jestjs/jest@8e6d128e4a (2026-08-10). Data as JSON: /api/errors/774c053d13fa77c4. Report an issue: GitHub.

Appendix: source

Thrown at packages/jest-config/src/index.ts:296

  for (const config of parsedConfigs) {
    const {configPath} = config;

    if (configPathMap.has(configPath)) {
      const message = `Whoops! Two projects resolved to the same config path: ${chalk.bold(
        String(configPath),
      )}:

  Project 1: ${chalk.bold(projects[parsedConfigs.indexOf(config)])}
  Project 2: ${chalk.bold(
    projects[parsedConfigs.indexOf(configPathMap.get(configPath))],
  )}

This usually means that your ${chalk.bold(
        '"projects"',
      )} config includes a directory that doesn't have any configuration recognizable by Jest. Please fix it.
`;

      throw new Error(message);
    }
    if (configPath !== null) {
      configPathMap.set(configPath, config);
    }
  }
};

export interface ReadJestConfigOptions {
  /**
   * The package root or deserialized config (default is cwd)
   */
  packageRootOrConfig?: string | Config.InitialOptions;
  /**
   * When the `packageRootOrConfig` contains config, this parameter should
   * contain the dirname of the parent config
   */
  parentConfigDirname?: null | string;
  /**

View on GitHub (pinned to 8e6d128e4a)