gastownhall/beads · error
loop %q: max must be positive
Error message
loop %q: max must be positive
What it means
validateLoopSpec rejects a LoopSpec whose Max field is negative when a formula using loops is expanded (via ApplyLoops). Max caps the number of loop iterations, so a negative value is meaningless. Note the check is `loop.Max < 0`, so zero actually passes validation despite the 'must be positive' wording.
Source
Thrown at internal/formula/controlflow.go:90
}
if loopTypes == 0 {
return fmt.Errorf("loop %q: one of count, until, or range is required", stepID)
}
if loopTypes > 1 {
return fmt.Errorf("loop %q: only one of count, until, or range can be specified", stepID)
}
if loop.Until != "" && loop.Max == 0 {
return fmt.Errorf("loop %q: max is required when until is set", stepID)
}
if loop.Count < 0 {
return fmt.Errorf("loop %q: count must be positive", stepID)
}
if loop.Max < 0 {
return fmt.Errorf("loop %q: max must be positive", stepID)
}
// Validate until condition syntax if present
if loop.Until != "" {
if _, err := ParseCondition(loop.Until); err != nil {
return fmt.Errorf("loop %q: invalid until condition %q: %w", stepID, loop.Until, err)
}
}
// Validate range syntax if present
if loop.Range != "" {
if err := ValidateRange(loop.Range); err != nil {
return fmt.Errorf("loop %q: invalid range %q: %w", stepID, loop.Range, err)
}
}
return nil
}View on GitHub (pinned to 71377f2769)
Solutions
- Fix the formula definition so loop.max is zero or a positive integer
- If max should mean 'unlimited', omit the field or use 0, since only negative values are rejected
- Check whether max is computed from a variable and verify that variable's resolved value
Example fix
# before loop: count: 3 max: -1 # after loop: count: 3 max: 10
Defensive patterns
Strategy: validation
Validate before calling
func validateLoopMax(loop *LoopSpec) error {
if loop != nil && loop.Max < 0 {
return fmt.Errorf("loop.max must be >= 0, got %d", loop.Max)
}
return nil
} Type guard
func hasValidMax(loop *LoopSpec) bool { return loop == nil || loop.Max >= 0 } Try / catch
steps, err := formula.ApplyLoops(steps)
if err != nil {
var msg string
if strings.Contains(err.Error(), "max must be positive") {
msg = "formula loop.max is negative; fix the loop spec"
}
return fmt.Errorf("loop config error: %v %s", err, msg)
} Prevention
- Never use negative max as a sentinel for 'unlimited'; omit the field instead
- Lint formula files for negative numeric fields in loop specs
- When computing max from variables, clamp with a lower bound of 0
When it happens
Trigger: Calling ApplyLoops (directly or via formula Apply/expand) on a Step whose Loop.Max is set to a negative number, typically from a YAML/formula file where max was typoed or computed from a negative expression.
Common situations: Hand-written formula YAML with `max: -1` intended as 'unlimited'; a max value computed from a variable that resolved negative; copy-paste of count/max config with a stray minus sign.
Related errors
- loop %q: invalid until condition %q: %w
- loop %q: invalid range %q: %w
- loop %q: range end (%d) is less than start (%d)
- applying control flow: %w
- formula %q not accessible: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/a9251ff17bae2c7b.
Report an issue: GitHub.