{"record":{"id":"394a45033daf1e10","repo":"gastownhall/beads","slug":"unexpected-token-in-expression","errorCode":null,"errorMessage":"unexpected token in expression","messagePattern":"unexpected token in expression","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/formula/range.go","lineNumber":343,"sourceCode":"func (p *exprParser) parsePrimary() (float64, error) {\n\tswitch p.current().typ {\n\tcase tokNumber:\n\t\tval := p.current().val\n\t\tp.advance()\n\t\treturn val, nil\n\tcase tokLParen:\n\t\tp.advance()\n\t\tval, err := p.parseAddSub()\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tif p.current().typ != tokRParen {\n\t\t\treturn 0, fmt.Errorf(\"expected closing parenthesis\")\n\t\t}\n\t\tp.advance()\n\t\treturn val, nil\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"unexpected token in expression\")\n\t}\n}\n\n// ValidateRange validates a range expression without evaluating it.\n// Useful for syntax checking during formula validation.\nfunc ValidateRange(expr string) error {\n\texpr = strings.TrimSpace(expr)\n\tif expr == \"\" {\n\t\treturn fmt.Errorf(\"empty range expression\")\n\t}\n\n\tm := rangePattern.FindStringSubmatch(expr)\n\tif m == nil {\n\t\treturn fmt.Errorf(\"invalid range format: expected start..end\")\n\t}\n\n\t// Check that expressions parse (with placeholder vars)\n\tplaceholderVars := make(map[string]string)","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/formula/range.go#L325-L361","documentation":"parsePrimary's default branch fires when the current token cannot start an operand — it is not a number, variable, or '('. This catches expressions beginning with an operator or containing a dangling operator where an operand is expected.","triggerScenarios":"EvaluateExpr with expressions like 'range 1..*3', '1..+5' where '+5' at operand position is fine but '*3' is not, or a variable pattern that tokenized unexpectedly leaving an operator at operand position.","commonSituations":"Binary operator typo at expression start ('*2..10'), variables expanding to nothing so an operator becomes the leading token, precedence mistakes in generated expressions.","solutions":["Ensure every operator has a left and right operand","Fix the expression so it starts with a number, variable, or '('","Validate with ValidateRange before evaluating"],"exampleFix":"// before\nrange 1..*3 { ... }\n// after\nrange 1..(2*3) { ... }","handlingStrategy":"validation","validationCode":"// reject expressions starting with a binary operator\nleading := regexp.MustCompile(`^\\s*[*/^]`)\nif leading.MatchString(expr) { /* reject */ }","typeGuard":null,"tryCatchPattern":"val, err := EvaluateExpr(expr, vars)\nif err != nil && strings.Contains(err.Error(), \"unexpected token in expression\") {\n    // treat as malformed user input; no retry\n}","preventionTips":["Ensure variable substitution never yields an empty operand","Start expressions with a number, variable, or '(' only","Validate with ValidateRange before evaluation"],"tags":["formula","parser","syntax","parsing"],"backgroundTag":"unexpected-token-in-expression","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}