gastownhall/beads · error

unexpected token in expression

Error message

unexpected token in expression

What it means

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.

Source

Thrown at internal/formula/range.go:343

func (p *exprParser) parsePrimary() (float64, error) {
	switch p.current().typ {
	case tokNumber:
		val := p.current().val
		p.advance()
		return val, nil
	case tokLParen:
		p.advance()
		val, err := p.parseAddSub()
		if err != nil {
			return 0, err
		}
		if p.current().typ != tokRParen {
			return 0, fmt.Errorf("expected closing parenthesis")
		}
		p.advance()
		return val, nil
	default:
		return 0, fmt.Errorf("unexpected token in expression")
	}
}

// ValidateRange validates a range expression without evaluating it.
// Useful for syntax checking during formula validation.
func ValidateRange(expr string) error {
	expr = strings.TrimSpace(expr)
	if expr == "" {
		return fmt.Errorf("empty range expression")
	}

	m := rangePattern.FindStringSubmatch(expr)
	if m == nil {
		return fmt.Errorf("invalid range format: expected start..end")
	}

	// Check that expressions parse (with placeholder vars)
	placeholderVars := make(map[string]string)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Ensure every operator has a left and right operand
  2. Fix the expression so it starts with a number, variable, or '('
  3. Validate with ValidateRange before evaluating

Example fix

// before
range 1..*3 { ... }
// after
range 1..(2*3) { ... }
Defensive patterns

Strategy: validation

Validate before calling

// reject expressions starting with a binary operator
leading := regexp.MustCompile(`^\s*[*/^]`)
if leading.MatchString(expr) { /* reject */ }

Try / catch

val, err := EvaluateExpr(expr, vars)
if err != nil && strings.Contains(err.Error(), "unexpected token in expression") {
    // treat as malformed user input; no retry
}

Prevention

When it happens

Trigger: 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.

Common situations: Binary operator typo at expression start ('*2..10'), variables expanding to nothing so an operator becomes the leading token, precedence mistakes in generated expressions.

Understand the failure class

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/394a45033daf1e10. Report an issue: GitHub.