{"record":{"id":"3d4f9814e546e7ef","repo":"vitest-dev/vitest","slug":"tag-name-tag-name-is-already-defined-in-test","errorCode":null,"errorMessage":"Tag name \"${tag.name}\" is already defined in \"test.tags\". Tag names must be unique.","messagePattern":"Tag name \"(.+?)\" is already defined in \"test\\.tags\"\\. Tag names must be unique\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/node/config/resolveConfig.ts","lineNumber":293,"sourceCode":"\n  if ('workspace' in resolved) {\n    throw new Error('The `test.workspace` option was removed in Vitest 4. Please, migrate to `test.projects` instead. See https://vitest.dev/guide/projects for examples.')\n  }\n\n  resolved.pool ??= 'forks'\n\n  resolved.project = toArray(resolved.project)\n  resolved.provide ??= {}\n\n  // shallow copy tags array to avoid mutating user config\n  resolved.tags = [...resolved.tags || []]\n  const definedTags = new Set<string>()\n  resolved.tags.forEach((tag) => {\n    if (!tag.name || typeof tag.name !== 'string') {\n      throw new Error(`Each tag defined in \"test.tags\" must have a \"name\" property, received: ${JSON.stringify(tag)}`)\n    }\n    if (definedTags.has(tag.name)) {\n      throw new Error(`Tag name \"${tag.name}\" is already defined in \"test.tags\". Tag names must be unique.`)\n    }\n    if (/\\s/.test(tag.name)) {\n      throw new Error(`Tag name \"${tag.name}\" is invalid. Tag names cannot contain spaces.`)\n    }\n    if (/[!()*|&]/.test(tag.name)) {\n      throw new Error(`Tag name \"${tag.name}\" is invalid. Tag names cannot contain \"!\", \"*\", \"&\", \"|\", \"(\", or \")\".`)\n    }\n    if (/^\\s*(?:and|or|not)\\s*$/i.test(tag.name)) {\n      throw new Error(`Tag name \"${tag.name}\" is invalid. Tag names cannot be a logical operator like \"and\", \"or\", \"not\".`)\n    }\n    if (typeof tag.retry === 'object' && typeof tag.retry.condition === 'function') {\n      throw new TypeError(`Tag \"${tag.name}\": retry.condition function cannot be used inside a config file. Use a RegExp pattern instead, or define the function in your test file.`)\n    }\n    if (tag.priority != null && (typeof tag.priority !== 'number' || tag.priority < 0)) {\n      throw new TypeError(`Tag \"${tag.name}\": priority must be a non-negative number.`)\n    }\n    definedTags.add(tag.name)\n  })","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/vitest-dev/vitest/blob/1fa9837ec26533512fdcad8baebf249771bd340a/packages/vitest/src/node/config/resolveConfig.ts#L275-L311","documentation":"Tag names within `test.tags` must be unique. Vitest accumulates names into a `Set` as it iterates and throws as soon as a duplicate `name` is encountered. Uniqueness is required because tags are referenced by name in test selection and filter expressions, where duplicates would be ambiguous.","triggerScenarios":"Two entries with the same `name` (`[{name:'smoke'},{name:'smoke'}]`); merging multiple tag sources (config preset + project config) that both define the same tag; case-sensitive duplicates that look distinct but resolve equal.","commonSituations":"Shared base config and per-project config both listing `smoke`; refactoring tags across packages without de-duplicating; CI matrix injecting tags that already exist.","solutions":["De-duplicate by name before exporting: `tags = [...new Map(tags.map(t => [t.name, t])).values()]`.","Move shared tags into a single config layer so they are not redefined per project.","If two tags genuinely differ, give them distinct names (e.g. `smoke-unit` vs `smoke-e2e`)."],"exampleFix":"// before\nexport default defineConfig({ test: { tags: [\n  { name: 'smoke' }, { name: 'smoke', color: 'red' },\n] } })\n\n// after\nexport default defineConfig({ test: { tags: [{ name: 'smoke', color: 'red' }] } })","handlingStrategy":"validation","validationCode":"function dedupeTags<T extends { name: string }>(tags: T[]): T[] {\n  const seen = new Set<string>()\n  const out: T[] = []\n  for (const t of tags) {\n    if (seen.has(t.name)) continue\n    seen.add(t.name)\n    out.push(t)\n  }\n  return out\n}\n\nexport default defineConfig({ test: { tags: dedupeTags(rawTags) } })","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Centralise tag definitions in one config layer to avoid cross-file duplication.","Run a dedupe pass when merging tags from presets and project config.","Use a Map keyed by name as the single source of truth."],"tags":["config","tags","duplicate","validation"],"backgroundTag":null,"analyzedSha":"1fa9837ec26533512fdcad8baebf249771bd340a","analyzedAt":"2026-08-11T16:11:39.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}