vitest-dev/vitest · error · Error

Invalid tags expression: missing closing ")" in

Error message

Invalid tags expression: missing closing ")" in "${this.expr}"

What it means

Thrown by TokenStream.expect when a closing parenthesis `)` is expected but the stream hit EOF first. It is a specialized, friendlier message for unbalanced parentheses in a `--tags` filter expression, surfaced instead of the generic 'expected ) but got end of expression' message.

Solutions

  1. Count parentheses and add the missing `)` at the end: `--tags '(foo && bar)'`.
  2. If nesting, ensure every `(` has a matching `)`.
  3. Simplify by removing unnecessary grouping parentheses.

Example fix

# before
vitest --tags '(smoke || critical'

# after
vitest --tags '(smoke || critical)'
Defensive patterns

Strategy: validation

Validate before calling

function balancedParens(expr: string): boolean {
  let depth = 0
  for (const ch of expr) {
    if (ch === '(') depth++
    else if (ch === ')') depth--
    if (depth < 0) return false
  }
  return depth === 0
}
if (!balancedParens(tagExpr)) throw new Error('Unbalanced parentheses in tags filter')

Prevention

When it happens

Trigger: A `--tags` expression with an unclosed `(`: `vitest --tags '(foo'`, `vitest --tags '(foo && bar'`, or `vitest --tags '!(foo || bar'`.

Common situations: Hand-typing a complex filter and missing a closing paren; editing a filter and deleting the trailing `)`; copy-truncation from a terminal.

Related errors


AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11). Data as JSON: /api/errors/19df35c33fb4bbe6. Report an issue: GitHub.

Appendix: source

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

    | { type: 'or'; left: ASTNode; right: ASTNode }

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'

View on GitHub (pinned to 1fa9837ec2)