gastownhall/beads · error
loop %q: count must be positive
Error message
loop %q: count must be positive
What it means
validateLoopSpec guards numeric loop bounds: a negative Count would make fixed-count expansion nonsensical (the expansion loop simply wouldn't run, silently dropping the body), so any negative Count is rejected with this error naming the step.
Source
Thrown at internal/formula/controlflow.go:86
loopTypes++
}
if loop.Range != "" {
loopTypes++
}
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)
}View on GitHub (pinned to 71377f2769)
Solutions
- Clamp or validate user-supplied counts to >=1 before constructing LoopSpec
- Sanitize computed counts: `if n < 1 { n = 1 }` or fall back to an until-loop for data-driven repetition
- Validate the formula file schema (count: integer >= 1) at load time with a clear message
Example fix
// before
count := len(items) - 1 // can be -1
loop := &formula.LoopSpec{Count: count, Body: body}
steps, err := formula.ApplyLoops(steps) // count must be positive
// after
count := len(items)
if count < 1 { count = 1 }
loop := &formula.LoopSpec{Count: count, Body: body}
steps, err := formula.ApplyLoops(steps) Defensive patterns
Strategy: validation
Validate before calling
n, err := strconv.Atoi(userCount)
if err != nil || n < 1 {
return fmt.Errorf("loop count must be a positive integer, got %q", userCount)
}
loop := &formula.LoopSpec{Count: n, Body: body}
steps, err := formula.ApplyLoops(steps) Type guard
func positiveInt(v int) bool { return v > 0 }
if loop != nil && loop.Count < 0 { /* reject before ApplyLoops */ } Try / catch
steps, err := formula.ApplyLoops(steps)
if err != nil {
if strings.Contains(err.Error(), "count must be positive") {
return fmt.Errorf("bad loop count: %w", err)
}
return err
} Prevention
- Validate user/config-supplied counts with strconv.Atoi and a >=1 lower bound
- Guard computed counts (e.g. len-1) against going negative
- Coerce 0/negative counts to 1 or switch to an until-loop for data-driven iteration
When it happens
Trigger: ApplyLoops with a LoopSpec where Count < 0 (e.g. -1 from a subtraction bug or user input like count: -2 in a formula file). Note Count<=0 otherwise falls into the 'one of count/until/range' check; negative values reach this check via loopTypes counting only Count>0.
Common situations: User-supplied count from config/UI accepted without range validation; arithmetic producing -1 (e.g. len(items)-1 passed as a repeat count); templating that interpolates an empty value into a number field.
Related errors
- applying control flow: %w
- loop %q: body is required
- loop %q: one of count, until, or range is required
- loop %q: only one of count, until, or range can be specified
- loop %q: max is required when until is set
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/33272cba67c23967.
Report an issue: GitHub.