vitest-dev/vitest · error · TypeError
Tag "${tag.name}": retry.condition function cannot be used i
Error message
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. What it means
Thrown when a tag defined in the config file (test.tags) sets retry.condition to a function. Vitest serializes config objects to send them across worker thread boundaries, and functions cannot survive serialization, so the condition would be silently dropped. The guard enforces that config-file tags only use serializable retry conditions (numbers, objects with RegExp patterns). The function form is permitted inside test files where the closure stays in-process.
Source
Thrown at packages/vitest/src/node/config/resolveConfig.ts:277
const definedTags = new Set<string>()
resolved.tags.forEach((tag) => {
if (!tag.name || typeof tag.name !== 'string') {
throw new Error(`Each tag defined in "test.tags" must have a "name" property, received: ${JSON.stringify(tag)}`)
}
if (definedTags.has(tag.name)) {
throw new Error(`Tag name "${tag.name}" is already defined in "test.tags". Tag names must be unique.`)
}
if (/\s/.test(tag.name)) {
throw new Error(`Tag name "${tag.name}" is invalid. Tag names cannot contain spaces.`)
}
if (/[!()*|&]/.test(tag.name)) {
throw new Error(`Tag name "${tag.name}" is invalid. Tag names cannot contain "!", "*", "&", "|", "(", or ")".`)
}
if (/^\s*(?:and|or|not)\s*$/i.test(tag.name)) {
throw new Error(`Tag name "${tag.name}" is invalid. Tag names cannot be a logical operator like "and", "or", "not".`)
}
if (typeof tag.retry === 'object' && typeof tag.retry.condition === 'function') {
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.`)
}
if (tag.priority != null && (typeof tag.priority !== 'number' || tag.priority < 0)) {
throw new TypeError(`Tag "${tag.name}": priority must be a non-negative number.`)
}
definedTags.add(tag.name)
})
resolved.name = typeof options.name === 'string'
? options.name
: (options.name?.label || '')
resolved.color = typeof options.name !== 'string' ? options.name?.color : undefined
if (resolved.environment === 'browser') {
throw new Error(`Looks like you set "test.environment" to "browser". To enable Browser Mode, use "test.browser.enabled" instead.`)
}
resolved.benchmark = {View on GitHub (pinned to d568f8ce37)
Solutions
- Replace the function with a RegExp pattern in the config: `retry: { count: 2, condition: /timeout/i }`.
- Move the tag with the function-based condition into the test file where it runs in-process: `test.configureTag('flaky', { retry: { count: 2, condition: fn } })`.
- Drop the condition and retry unconditionally via a number: `retry: 2`.
Example fix
// before (vitest.config.ts)
export default defineConfig({ test: { tags: [{ name: 'flaky', retry: { count: 2, condition: (r) => r.duration > 1000 } }] } })
// after
export default defineConfig({ test: { tags: [{ name: 'flaky', retry: { count: 2, condition: /timeout/i } }] } }) Defensive patterns
Strategy: validation
Validate before calling
// before passing config to vitest
import type { RawTags } from 'vitest/node'
function assertSerializableTags(tags: RawTags[] | undefined) {
for (const t of tags ?? []) {
if (t.retry && typeof t.retry === 'object' && typeof (t.retry as any).condition === 'function') {
throw new Error(`Tag "${t.name}": retry.condition must be a RegExp in config, not a function.`)
}
}
} Type guard
function isConfigSafeTag(tag: any): tag is { name: string; retry?: number | { condition?: RegExp; count?: number } } {
return !(tag.retry && typeof tag.retry === 'object' && typeof tag.retry.condition === 'function')
} Prevention
- Treat config-file values as serializable: never put functions in test.tags.
- Keep function-based retry conditions in test files via test.configureTag.
- Add a pre-flight lint rule that rejects `retry: { condition:` arrow functions in config files.
When it happens
Trigger: In vitest.config.ts, set `tags: [{ name: 'flaky', retry: { count: 2, condition: (result) => result.errors > 0 } }]`. The check at resolveConfig.ts:276 fires because `tag.retry` is an object and `tag.retry.condition` is a function.
Common situations: Copying a retry condition from a test file into a shared config; writing a project-wide tag policy with custom logic; upgrading from a version where this was unguarded.
Related errors
- "snapshotFormat.compareKeys" function is not supported.
- Tag "${tag.name}": priority must be a non-negative number.
- Unknown value for "test.listTags": ${listTags}
- The Vitest config does't define any "tags", cannot apply "${
- The ${prefix} "${tag}" is not defined in the configuration.
AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03).
Data as JSON: /data/errors/ccfd85d4a28784bb.json.
Report an issue: GitHub.