vitest-dev/vitest · error · Error
Invalid tags expression: unexpected
Error message
Invalid tags expression: unexpected "${formatToken(stream.peek())}" in "${expr}" What it means
Thrown by parseTagsExpression after a top-level OR-expression parse completes but the token stream still has a non-EOF token. This indicates the expression parsed successfully up to a point and then an unexpected token followed that cannot extend a valid expression (e.g. two consecutive tags with no operator). It governs the `--tags` CLI filter grammar, which supports tag names plus `&&`/`||`/`!`/parentheses and the words and/or/not.
Solutions
- Join multiple required tags with `&&` or the word `and`: `--tags 'foo && bar'`.
- Join alternatives with `||` or `or`: `--tags 'foo || bar'`.
- Remove the stray token the error points at (the unexpected token is quoted in the message).
- Wrap sub-expressions in parentheses only as complete units: `--tags '(foo || bar) && baz'`.
Example fix
# before vitest --tags 'smoke auth' # after vitest --tags 'smoke && auth'
Defensive patterns
Strategy: validation
Validate before calling
// Validate a --tags expression before passing to vitest
import { execFileSync } from 'node:child_process'
// Cheap pre-check: every pair of tags must be separated by an operator
function looksBalanced(expr: string): boolean {
const ops = expr.split(/\s*(?:&&|\|\||\band\b|\bor\b)\s*/i)
return ops.every(p => p.trim().length > 0)
} Try / catch
// In a wrapper script that invokes vitest --tags
try {
runVitest(['--tags', expr])
} catch (e) {
if (String(e.message).startsWith('Invalid tags expression')) {
console.error('Tags filter syntax error. Use tag && tag, tag || tag, !tag, ( ... ).')
process.exit(2)
}
throw e
} Prevention
- Reuse a small, documented set of tag-filter snippets rather than hand-typing each time.
- When joining tags programmatically, always insert an operator: parts.join(' && ').
- Keep a cheat-sheet of the grammar (tags, && / || / !, parentheses, and/or/not words) near your run scripts.
When it happens
Trigger: Running `vitest --tags 'foo bar'` (two tags with no operator between them), `vitest --tags 'foo )'` (stray closing paren after a tag), or any expression where a second complete sub-expression follows the first without `&&`/`||`/`and`/`or`.
Common situations: Users assume multiple tags are space-separated (AND) like some other tools; misplaced parentheses; pasting a filter expression from documentation that used a different syntax.
Related errors
- Invalid tags expression: expected
- Invalid tags expression: missing closing ")" in
- Invalid tags expression: unexpected end of expression in
- Invalid tags expression: unexpected
- Unknown value for "test.listTags
AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11).
Data as JSON: /api/errors/683b68d1e649511f.
Report an issue: GitHub.
Appendix: source
Thrown at packages/vitest/src/runtime/runner/utils/tags.ts:60
.map(t => `- ${t.name}${t.description ? `: ${t.description}` : ''}`)
.join('\n')}`)
}
export function createTagsFilter(tagsExpr: string[], availableTags: TestTagDefinition[]): (testTags: string[]) => boolean {
const matchers = tagsExpr.map(expr => parseTagsExpression(expr, availableTags))
return (testTags: string[]) => {
return matchers.every(matcher => matcher(testTags))
}
}
type TagMatcher = (tags: string[]) => boolean
function parseTagsExpression(expr: string, availableTags: TestTagDefinition[]): TagMatcher {
const tokens = tokenize(expr)
const stream = new TokenStream(tokens, expr)
const ast = parseOrExpression(stream, availableTags)
if (stream.peek().type !== 'EOF') {
throw new Error(`Invalid tags expression: unexpected "${formatToken(stream.peek())}" in "${expr}"`)
}
return (tags: string[]) => evaluateNode(ast, tags)
}
function formatToken(token: Token): string {
switch (token.type) {
case 'TAG': return token.value
default: return formatTokenType(token.type)
}
}
type Token
= | { type: 'TAG'; value: string }
| { type: 'AND' }
| { type: 'OR' }
| { type: 'NOT' }
| { type: 'LPAREN' }
| { type: 'RPAREN' }View on GitHub (pinned to 1fa9837ec2)