{"record":{"id":"ccfd85d4a28784bb","repo":"vitest-dev/vitest","slug":"tag-tag-name-retry-condition-function-cannot","errorCode":null,"errorMessage":"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.","messagePattern":"Tag \"(.+?)\": retry\\.condition function cannot be used inside a config file\\. Use a RegExp pattern instead, or define the function in your test file\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/node/config/resolveConfig.ts","lineNumber":305,"sourceCode":"  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  })\n\n  resolved.name = typeof options.name === 'string'\n    ? options.name\n    : (options.name?.label || '')\n\n  resolved.color = typeof options.name !== 'string' ? options.name?.color : undefined\n\n  if (resolved.environment === 'browser') {\n    throw new Error(`Looks like you set \"test.environment\" to \"browser\". To enable Browser Mode, use \"test.browser.enabled\" instead.`)\n  }\n\n  resolved.benchmark = {","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/vitest-dev/vitest/blob/1fa9837ec26533512fdcad8baebf249771bd340a/packages/vitest/src/node/config/resolveConfig.ts#L287-L323","documentation":"A tag's `retry.condition` may not be a function when defined in a config file, because config files are serialized/shared across worker boundaries where functions cannot travel. Vitest detects `typeof tag.retry === 'object' && typeof tag.retry.condition === 'function'` and throws a `TypeError`, asking the user to use a RegExp pattern or move the function into the test file.","triggerScenarios":"`test.tags: [{ name: 'flaky', retry: { condition: (err) => err.message.includes('timeout') } }]` in `vitest.config.ts`. The same shape is fine when declared inside a test file via the runner API, because that code runs in-process.","commonSituations":"Migrating per-test retry conditions into shared config; reusing a function from a config preset; refactoring that moves logic from test files into config.","solutions":["Replace the function with a RegExp pattern over the error message if the config supports it.","Move the conditional retry into the test file where the function executes in-process.","If a RegExp is insufficient, narrow the retry scope to specific tests instead of a tag."],"exampleFix":"// before — vitest.config.ts\nexport default defineConfig({ test: { tags: [{\n  name: 'flaky',\n  retry: { condition: (e: Error) => /timeout/.test(e.message) },\n}] } })\n\n// after — use a RegExp pattern in config\nexport default defineConfig({ test: { tags: [{\n  name: 'flaky',\n  retry: { condition: /timeout/ },\n}] } })\n// or move the function into the test file and attach retry there","handlingStrategy":"validation","validationCode":"type RetryConfig = { condition?: RegExp; limit?: number }\nfunction assertConfigSafeRetry(retry: unknown): void {\n  if (retry && typeof retry === 'object'\n      && 'condition' in (retry as any)\n      && typeof (retry as any).condition === 'function') {\n    throw new TypeError('retry.condition cannot be a function in a config file; use a RegExp')\n  }\n}\n\n(rawTags ?? []).forEach(t => assertConfigSafeRetry(t.retry))","typeGuard":"function isConfigSafeRetry(r: unknown): r is RetryConfig {\n  if (r == null) return true\n  if (typeof r !== 'object') return false\n  const cond = (r as any).condition\n  return cond === undefined || cond instanceof RegExp\n}","tryCatchPattern":null,"preventionTips":["Use RegExp patterns for `retry.condition` in config files.","Move function-based conditions into the test file where they run in-process.","Lint configs for function-valued properties on serialisable config shapes."],"tags":["config","tags","retry","serialization","validation"],"backgroundTag":null,"analyzedSha":"1fa9837ec26533512fdcad8baebf249771bd340a","analyzedAt":"2026-08-11T16:11:39.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}