{"record":{"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/1fa9837ec26533512fdcad8baebf249771bd340a/packages/vitest/src/runtime/runner/utils/tags.ts#L176-L212","documentation":"Thrown by TokenStream.unexpectedToken when the parser is at a primary-expression position and the current token is neither a tag nor a left-paren (and is not EOF, which is handled separately by error 345). The message names the offending token so the user can locate it.","triggerScenarios":"A `--tags` filter starting with a binary operator (`vitest --tags '&& foo'`), a `)` with no matching `(` (`vitest --tags 'foo)'` — though this often hits 342), a leading operator inside a group (`--tags '(&& foo)'`), or any token that cannot begin a primary expression.","commonSituations":"Pasting an expression and losing its first token; building filters dynamically and joining with a leading `&&`/`||`; stray punctuation; mismatched parentheses producing a `)` where a value is expected.","solutions":["Start the expression (and every sub-expression) with a tag name or `(`: `--tags 'foo'` or `--tags '(foo || bar)'`.","Remove the leading/stray operator identified by 'unexpected' in the message.","When building filters in code, join non-empty fragments only: `parts.filter(Boolean).join(' && ')`."],"exampleFix":"# before\nvitest --tags '&& smoke'\n\n# after\nvitest --tags 'smoke'","handlingStrategy":"validation","validationCode":"function startsWithTagOrParen(expr: string): boolean {\n  return /^\\s*(?:!?[\\w*-]+|\\()/.test(expr)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never begin an expression (or a sub-expression after `(`) with an operator.","When building filters dynamically, trim leading operators: s.replace(/^(?:&&|\\|\\|)+/, '').","Validate generated expressions with a tiny parser/test before passing to vitest."],"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"}