vitest-dev/vitest · error · Error

Invalid tags expression: unexpected "${formatToken(token)}"

Error message

Invalid tags expression: unexpected "${formatToken(token)}" in "${this.expr}"

What it means

Thrown by TokenStream.unexpectedToken when the parser needs a primary operand but finds a non-EOF token that cannot start one (a ')', '&&', '||', or a stray operator in the wrong position). It is the general 'I did not expect this here' parser error.

Source

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

  }

  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'
    case 'NOT': return 'not'
    case 'LPAREN': return '('
    case 'RPAREN': return ')'
    case 'EOF': return 'end of expression'
  }
}

function parseOrExpression(stream: TokenStream, availableTags: TestTagDefinition[]): ASTNode {
  let left = parseAndExpression(stream, availableTags)

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Make sure the expression starts with a tag, '!', or '('.
  2. Never place two operators adjacent to each other.
  3. Ensure every '(' contains at least one complete sub-expression.
  4. Rewrite the filter as a clean boolean tree of tags joined by && / || / !.

Example fix

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

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

Strategy: validation

Validate before calling

// Reject expressions that start with a binary operator or contain empty groups
function isWellFormedStart(expr: string): boolean {
  const t = expr.trim()
  if (/^(?:&&|\|\|)/.test(t)) return false
  if (/\(\s*\)/.test(t)) return false
  return true
}

Prevention

When it happens

Trigger: `--tag '&& unit'` (expression starts with AND). `--tag 'unit (smoke)'` (group right after a tag with no operator). `--tag '()'` (empty group). `--tag 'unit && && smoke'` (two operators in a row).

Common situations: Misunderstanding that an expression must start with a tag, '!', or '('. Joining operators incorrectly. Empty parentheses.

Related errors


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