vitest-dev/vitest · error · Error

Invalid tags expression: expected "${formatTokenType(type)}"

Error message

Invalid tags expression: expected "${formatTokenType(type)}" but got "${formatToken(token)}" in "${this.expr}"

What it means

Thrown by TokenStream.expect when a specific token type was required but a different one appeared. Currently the only path that reaches a non-RPAREN expect is expect('RPAREN') hitting a non-EOF token (line 184 fires for that case), so in practice this message means: expected a ')' but got some other token (a tag, '&&', etc.) before end of input.

Source

Thrown at packages/vitest/src/runtime/runner/utils/tags.ts:184

class TokenStream {
  private pos = 0
  constructor(private tokens: Token[], public expr: string) {}

  peek(): Token {
    return this.tokens[this.pos]
  }

  next(): Token {
    return this.tokens[this.pos++]
  }

  expect(type: Token['type']): Token {
    const token = this.next()
    if (token.type !== type) {
      if (type === 'RPAREN' && token.type === 'EOF') {
        throw new Error(`Invalid tags expression: missing closing ")" in "${this.expr}"`)
      }
      throw new Error(`Invalid tags expression: expected "${formatTokenType(type)}" but got "${formatToken(token)}" in "${this.expr}"`)
    }
    return token
  }

  unexpectedToken(): never {
    const token = this.peek()
    if (token.type === 'EOF') {
      throw new Error(`Invalid tags expression: unexpected end of expression in "${this.expr}"`)
    }
    throw new Error(`Invalid tags expression: unexpected "${formatToken(token)}" in "${this.expr}"`)
  }
}

function formatTokenType(type: Token['type']): string {
  switch (type) {
    case 'TAG': return 'tag'
    case 'AND': return 'and'
    case 'OR': return 'or'

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Insert the missing operator between tokens inside the group: `(a && b)`.
  2. Remove the unexpected token so the inner expression is a single complete or/and/not tree.
  3. Validate the expression is a single balanced boolean tree of tags and operators.

Example fix

// before
vitest --tag '(unit smoke)'

// after
vitest --tag '(unit || smoke)'
Defensive patterns

Strategy: validation

Validate before calling

// Reject expressions where a TAG is immediately followed by another TAG or by ')'
function hasAdjacentOperands(expr: string): boolean {
  return /(\b\w+\b)\s+(\b\w+\b)/.test(expr) // crude: flag for manual review
}

Prevention

When it happens

Trigger: A tag filter like `--tag '(unit smoke)'` where the inner expression `unit smoke` has two tags with no operator; after parsing 'unit' the and/or loops stop at the bare 'smoke' token, then expect(RPAREN) finds the 'smoke' TAG token instead of ')'.

Common situations: Putting two tags inside parentheses without an operator: `(a b)`. Adding an operator before the closing paren like `(a && )`.

Related errors


AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03). Data as JSON: /data/errors/cc39c0d2aee95435.json. Report an issue: GitHub.