{"id":"512a729cfec9e8db","repo":"vitest-dev/vitest","slug":"each-tag-defined-in-test-tags-must-have-a-name","errorCode":null,"errorMessage":"Each tag defined in \"test.tags\" must have a \"name\" property, received: ${JSON.stringify(tag)}","messagePattern":"Each tag defined in \"test\\.tags\" must have a \"name\" property, received: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/node/config/resolveConfig.ts","lineNumber":262,"sourceCode":"  if ('poolOptions' in resolved) {\n    logger.deprecate('`test.poolOptions` was removed in Vitest 4. All previous `poolOptions` are now top-level options. Please, refer to the migration guide: https://v4.vitest.dev/guide/migration#pool-rework')\n  }\n\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.`)","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/vitest/src/node/config/resolveConfig.ts#L244-L280","documentation":"Thrown during tag validation when an element of `test.tags` lacks a string `name` property. Tags are referenced by name in test filtering expressions and CLI `--testTags`, so every tag object must have a non-empty string name. A tag without a name cannot be selected and is rejected up front.","triggerScenarios":"Defining `test.tags` with an entry like `{}` (empty object), `{ name: 123 }` (non-string), or `{ color: 'red' }` (missing name). The check `!tag.name || typeof tag.name !== 'string'` triggers, and JSON.stringify of the offending tag is included.","commonSituations":"Building tags programmatically and forgetting the name; partial tag objects from a preset; copy-paste that left only metadata fields; refactoring that renamed `name` to `label`.","solutions":["Give every tag a string `name`: `test: { tags: [{ name: 'slow' }] }`.","If generating tags from data, map and assert each has a string name before assigning to config.","Validate the tags array in a unit test of your config builder."],"exampleFix":"// before\nexport default defineConfig({ test: { tags: [{ color: 'red' }] } })\n// after\nexport default defineConfig({ test: { tags: [{ name: 'smoke', color: 'red' }] } })","handlingStrategy":"type-guard","validationCode":"for (const tag of tags) {\n  if (typeof tag?.name !== 'string' || tag.name.length === 0) {\n    throw new Error(`tag missing string name: ${JSON.stringify(tag)}`)\n  }\n}","typeGuard":"function isTag(t: unknown): t is { name: string;[k: string]: unknown } {\n  return typeof t === 'object' && t !== null\n    && typeof (t as any).name === 'string' && (t as any).name.length > 0\n}","tryCatchPattern":null,"preventionTips":["Give every tag a non-empty string name.","When building tags from data, validate with isTag before assigning.","Unit-test config builders that produce the tags array."],"tags":[],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}