gastownhall/beads · error

evaluating range end %q: %w

Error message

evaluating range end %q: %w

What it means

ParseRange() evaluated the start side fine but EvaluateExpr failed on the end expression, so the end side of the range is not a valid arithmetic expression after variable substitution. The failing end expression and the evaluator's cause are wrapped in the error.

Source

Thrown at internal/formula/range.go:66

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

	startExpr := strings.TrimSpace(m[1])
	endExpr := strings.TrimSpace(m[2])

	// Evaluate start expression
	start, err := EvaluateExpr(startExpr, vars)
	if err != nil {
		return nil, fmt.Errorf("evaluating range start %q: %w", startExpr, err)
	}

	// Evaluate end expression
	end, err := EvaluateExpr(endExpr, vars)
	if err != nil {
		return nil, fmt.Errorf("evaluating range end %q: %w", endExpr, err)
	}

	return &RangeSpec{Start: start, End: end}, nil
}

// EvaluateExpr evaluates a mathematical expression with variable substitution.
// Supports: + - * / ^ (power) and parentheses.
// Variables use {name} syntax.
func EvaluateExpr(expr string, vars map[string]string) (int, error) {
	// Substitute variables first
	expr = substituteVars(expr, vars)

	// Tokenize and parse
	tokens, err := tokenize(expr)
	if err != nil {
		return 0, err
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Fix the end expression to valid arithmetic, e.g. "1..10" or "1..2^{n}".
  2. Ensure the variables referenced on the end side are present in the vars map and numeric after substitution.
  3. Inspect the inner EvaluateExpr error to pinpoint whether it is an unknown variable or invalid token.

Example fix

# before
range: "1..{count}"   # count = "auto"

# after
vars: { count: "10" }
range: "1..{count}"
Defensive patterns

Strategy: validation

Validate before calling

// Check that every {var} in the end expression exists and is numeric.
for _, m := range varRefRe.FindAllStringSubmatch(endExpr, -1) {
	v, ok := vars[m[1]]
	if !ok {
		return fmt.Errorf"undefined var %q in range end", m[1]
	}
	if _, err := strconv.Atoi(strings.TrimSpace(v)); err != nil {
		return fmt.Errorf"var %q=%q is not numeric", m[1], v
	}
}

Try / catch

spec, err := ParseRange(expr, vars)
if err != nil {
	if strings.Contains(err.Error(), "evaluating range end") {
		// fix the end expression or the vars it references
	}
}

Prevention

When it happens

Trigger: ParseRange("1..b", vars) with 'b' undefined; end expressions containing unsupported operators or non-numeric residue; unexpanded placeholders like "1..2^{k}" where k is absent from vars; expandLoopWithVars passing a partially-substituted end expression.

Common situations: Count-driven loops ('1..{count}') where the count var was never set or was set to a non-numeric string like 'auto'; typos in the end-side variable name; copy-paste leaving currency symbols or whitespace tokens in the expression.

Related errors


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