gastownhall/beads · error
unexpected token after expression
Error message
unexpected token after expression
What it means
After parsing the full additive expression, parseExpr (the entry point of the expression parser) checks that only the EOF token remains. If extra tokens follow a complete expression, the input is ambiguous/trailing garbage and parsing fails with this error. It guards EvaluateExpr against silently ignoring trailing input.
Source
Thrown at internal/formula/range.go:226
return token{tokEOF, 0}
}
return p.tokens[p.pos]
}
func (p *exprParser) advance() {
p.pos++
}
// parseExpr parses an expression using recursive descent.
// Handles operator precedence: + - < * / < ^
func parseExpr(tokens []token) (float64, error) {
p := &exprParser{tokens: tokens}
result, err := p.parseAddSub()
if err != nil {
return 0, err
}
if p.current().typ != tokEOF {
return 0, fmt.Errorf("unexpected token after expression")
}
return result, nil
}
// parseAddSub handles + and - (lowest precedence)
func (p *exprParser) parseAddSub() (float64, error) {
left, err := p.parseMulDiv()
if err != nil {
return 0, err
}
for {
switch p.current().typ {
case tokPlus:
p.advance()
right, err := p.parseMulDiv()
if err != nil {
return 0, errView on GitHub (pinned to 71377f2769)
Solutions
- Fix the expression so there is an operator between every pair of operands
- Remove trailing garbage after the expression
- Test the expression with ValidateRange before evaluation
Example fix
// before
range 1..(10)5 { ... }
// after
range 1..(10)*5 { ... } Defensive patterns
Strategy: validation
Validate before calling
balanced := 0
for _, r := range expr { if r=='(' {balanced++}; if r==')' {balanced--} }
// also ensure no two operands are separated only by whitespace:
o predatory := regexp.MustCompile(`\d\s+\d`)
if balanced != 0 || operandGap.MatchString(expr) { /* reject */ } Try / catch
val, err := EvaluateExpr(expr, vars)
if err != nil && strings.Contains(err.Error(), "unexpected token after expression") {
// report malformed expression; do not retry
} Prevention
- Insert explicit operators when concatenating expression fragments
- Validate ranges with ValidateRange before EvaluateExpr
- Avoid templating that can emit two adjacent values without an operator
When it happens
Trigger: EvaluateExpr with an expression that has trailing tokens after a valid expression, e.g. '1 2', '3 + 4 5', or an unbalanced construct like '(1+2))' where the parser finishes before consuming everything.
Common situations: Missing operators between operands ('2 3' instead of '2*3'), duplicated expressions from templating bugs, or accidental concatenation when building expressions programmatically.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- unexpected token in expression
- unexpected character %q in expression
- expected closing parenthesis
- parsing formula: %w
- parsing JSON: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/ab37da2ef70bde35.
Report an issue: GitHub.