{"record":{"id":"19df35c33fb4bbe6","repo":"vitest-dev/vitest","slug":"invalid-tags-expression-missing-closing-in","errorCode":null,"errorMessage":"Invalid tags expression: missing closing \")\" in \"${this.expr}\"","messagePattern":"Invalid tags expression: missing closing \"\\)\" in \"(.+?)\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/runtime/runner/utils/tags.ts","lineNumber":182,"sourceCode":"    | { type: 'or'; left: ASTNode; right: ASTNode }\n\nclass TokenStream {\n  private pos = 0\n  constructor(private tokens: Token[], public expr: string) {}\n\n  peek(): Token {\n    return this.tokens[this.pos]\n  }\n\n  next(): Token {\n    return this.tokens[this.pos++]\n  }\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'","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/vitest-dev/vitest/blob/1fa9837ec26533512fdcad8baebf249771bd340a/packages/vitest/src/runtime/runner/utils/tags.ts#L164-L200","documentation":"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.","triggerScenarios":"A `--tags` expression with an unclosed `(`: `vitest --tags '(foo'`, `vitest --tags '(foo && bar'`, or `vitest --tags '!(foo || bar'`.","commonSituations":"Hand-typing a complex filter and missing a closing paren; editing a filter and deleting the trailing `)`; copy-truncation from a terminal.","solutions":["Count parentheses and add the missing `)` at the end: `--tags '(foo && bar)'`.","If nesting, ensure every `(` has a matching `)`.","Simplify by removing unnecessary grouping parentheses."],"exampleFix":"# before\nvitest --tags '(smoke || critical'\n\n# after\nvitest --tags '(smoke || critical)'","handlingStrategy":"validation","validationCode":"function balancedParens(expr: string): boolean {\n  let depth = 0\n  for (const ch of expr) {\n    if (ch === '(') depth++\n    else if (ch === ')') depth--\n    if (depth < 0) return false\n  }\n  return depth === 0\n}\nif (!balancedParens(tagExpr)) throw new Error('Unbalanced parentheses in tags filter')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use an editor that highlights matching parentheses.","For generated filters, wrap each group and close it on the same edit.","Prefer flat expressions (a && b || c) when grouping is not required."],"tags":["cli","tags","parser","validation"],"backgroundTag":null,"analyzedSha":"1fa9837ec26533512fdcad8baebf249771bd340a","analyzedAt":"2026-08-11T16:11:39.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}