{"id":"ae9626a1e7aa4658","repo":"vitest-dev/vitest","slug":"project-name-name-entryfile-from-entr","errorCode":null,"errorMessage":"Project name \"${name}\"${entryFile ? ` from \"${entryFile}\"` : ''} is not unique.${existingFile ? ` The project is already defined by \"${existingFile}\".` : ''}${filesError}All projects should have unique names. Make sure your configuration is correct.","messagePattern":"Project name \"(.+?)\"(.+?)\"` : ''\\} is not unique\\.(.+?)\"\\.` : ''\\}(.+?)All projects should have unique names\\. Make sure your configuration is correct\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/node/projects/resolveProjects.ts","lineNumber":170,"sourceCode":"      // inline entries carry the configFile they extend, which doesn't say\n      // where the project is declared, so they are reported without a file\n      const entryFile = !entry.inline && entry.viteConfig.configFile\n        ? relative(globalConfig.root, entry.viteConfig.configFile)\n        : ''\n      const existingFile = !existing.inline && existing.viteConfig.configFile\n        ? relative(globalConfig.root, existing.viteConfig.configFile)\n        : ''\n      const filesError = baseEntries.length > 1 && (entryFile || existingFile)\n        ? [\n            '\\n\\nYour config matched these files:\\n',\n            baseEntries\n              .filter(e => !e.inline && e.viteConfig.configFile)\n              .map(e => ` - ${relative(globalConfig.root, e.viteConfig.configFile as string)}`)\n              .join('\\n'),\n            '\\n\\n',\n          ].join('')\n        : ' '\n      throw new Error([\n        `Project name \"${name}\"`,\n        entryFile ? ` from \"${entryFile}\"` : '',\n        ' is not unique.',\n        existingFile ? ` The project is already defined by \"${existingFile}\".` : '',\n        filesError,\n        'All projects should have unique names. Make sure your configuration is correct.',\n      ].join(''))\n    }\n    seenNames.set(name, entry)\n  }\n  const seenNamesSet = new Set(seenNames.keys())\n\n  // Browser instance expansion (per-entry config injection).\n  const afterBrowser = expandBrowserInstancesInEntries(globalConfig, baseEntries, seenNamesSet)\n\n  // Benchmark expansion (per-entry config injection, runs over post-browser list).\n  // `--benchmark` makes every project run as a benchmark.\n  const afterBenchmark = expandBenchmarksInEntries(afterBrowser, seenNamesSet, !!globalConfig.cliOptions.benchmarkOnly)","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/vitest/src/node/projects/resolveProjects.ts#L152-L188","documentation":"The detailed duplicate-name error (resolveProjects.ts:150-178), thrown when two entries resolved in the same batch share a project name. It augments the message with the config files that produced each entry and, when multiple file-based entries exist, lists every config file considered so you can see which file matched unexpectedly.","triggerScenarios":"Two inline project configs with the same test.name; two config files whose package.json name / directory basename resolve identically; a glob like 'packages/*' matching multiple directories that each fall back to the same auto-name; the same config file referenced twice.","commonSituations":"Monorepo where two packages have the same name in their package.json; renaming a package but forgetting the workspace entry; a glob that matches both a directory and a sibling config file producing the same resolved name; explicit test.name reused across entries.","solutions":["Give each project an explicit unique test.name in its config.","If using auto-naming, fix duplicate package.json names or directory basenames.","Inspect the listed config files to find which entries collided and remove or rename the duplicate.","For globs, tighten the pattern so only one entry per intended project matches."],"exampleFix":"// before: both packages auto-named 'core' via package.json\nexport default defineConfig({ test: { projects: ['packages/core-old', 'packages/core-new'] } })\n\n// after\nexport default defineConfig({\n  test: {\n    projects: [\n      { test: { name: 'core-old', root: 'packages/core-old' } },\n      { test: { name: 'core-new', root: 'packages/core-new' } },\n    ],\n  },\n})","handlingStrategy":"validation","validationCode":"const names = definitions.map(d => typeof d === 'string' ? basename(dirname(d)) : (d as any).test?.name ?? '(auto)')\nconst dupes = names.filter((n, i) => names.indexOf(n) !== i)\nif (dupes.length) throw new Error(`Duplicate project names: ${[...new Set(dupes)].join(', ')}`)","typeGuard":"function allUnique<T>(arr: T[]): boolean {\n  return new Set(arr).size === arr.length\n}","tryCatchPattern":null,"preventionTips":["Set explicit, unique test.name on every project config.","In monorepos, ensure package.json names differ.","For globs, sanity-check the matched directories' names before running."],"tags":["projects","naming","duplicate","configuration","monorepo"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}