{"id":"3c8a25a7767e4162","repo":"vitest-dev/vitest","slug":"invalid-tags-expression-unexpected-formattoken-3c8a25","errorCode":null,"errorMessage":"Invalid tags expression: unexpected \"${formatToken(token)}\" in \"${this.expr}\"","messagePattern":"Invalid tags expression: unexpected \"(.+?)\" in \"(.+?)\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/runtime/runner/utils/tags.ts","lineNumber":194,"sourceCode":"  }\n\n  expect(type: Token['type']): Token {\n    const token = this.next()\n    if (token.type !== type) {\n      if (type === 'RPAREN' && token.type === 'EOF') {\n        throw new Error(`Invalid tags expression: missing closing \")\" in \"${this.expr}\"`)\n      }\n      throw new Error(`Invalid tags expression: expected \"${formatTokenType(type)}\" but got \"${formatToken(token)}\" in \"${this.expr}\"`)\n    }\n    return token\n  }\n\n  unexpectedToken(): never {\n    const token = this.peek()\n    if (token.type === 'EOF') {\n      throw new Error(`Invalid tags expression: unexpected end of expression in \"${this.expr}\"`)\n    }\n    throw new Error(`Invalid tags expression: unexpected \"${formatToken(token)}\" in \"${this.expr}\"`)\n  }\n}\n\nfunction formatTokenType(type: Token['type']): string {\n  switch (type) {\n    case 'TAG': return 'tag'\n    case 'AND': return 'and'\n    case 'OR': return 'or'\n    case 'NOT': return 'not'\n    case 'LPAREN': return '('\n    case 'RPAREN': return ')'\n    case 'EOF': return 'end of expression'\n  }\n}\n\nfunction parseOrExpression(stream: TokenStream, availableTags: TestTagDefinition[]): ASTNode {\n  let left = parseAndExpression(stream, availableTags)\n","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/vitest/src/runtime/runner/utils/tags.ts#L176-L212","documentation":"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.","triggerScenarios":"`--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).","commonSituations":"Misunderstanding that an expression must start with a tag, '!', or '('. Joining operators incorrectly. Empty parentheses.","solutions":["Make sure the expression starts with a tag, '!', or '('.","Never place two operators adjacent to each other.","Ensure every '(' contains at least one complete sub-expression.","Rewrite the filter as a clean boolean tree of tags joined by && / || / !."],"exampleFix":"// before\nvitest --tag '&& unit'\nvitest --tag '()'\n\n// after\nvitest --tag 'unit'\nvitest --tag '(unit && smoke)'","handlingStrategy":"validation","validationCode":"// Reject expressions that start with a binary operator or contain empty groups\nfunction isWellFormedStart(expr: string): boolean {\n  const t = expr.trim()\n  if (/^(?:&&|\\|\\|)/.test(t)) return false\n  if (/\\(\\s*\\)/.test(t)) return false\n  return true\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Start every expression with a tag, '!', or '('.","Never place two operators adjacent to each other.","Never use empty parentheses ()."],"tags":["tags","filter-expression","parser"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}