{"record":{"id":"5b757d087b6af26c","repo":"gastownhall/beads","slug":"loop-q-range-end-d-is-less-than-start-d","errorCode":null,"errorMessage":"loop %q: range end (%d) is less than start (%d)","messagePattern":"loop %q: range end \\((.+?)\\) is less than start \\((.+?)\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/formula/controlflow.go","lineNumber":151,"sourceCode":"\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// THEN chain iterations on the expanded result\n\t\t// This must happen AFTER recursive expansion so we chain the final steps\n\t\tif step.Loop.Count > 1 {\n\t\t\tresult = chainExpandedIterations(result, step.ID, step.Loop.Count)\n\t\t}\n\t} else if step.Loop.Range != \"\" {\n\t\t// Range loop: expand body for each value in the computed range\n\t\trangeSpec, err := ParseRange(step.Loop.Range, vars)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"loop %q: %w\", step.ID, err)\n\t\t}\n\n\t\t// Validate range\n\t\tif rangeSpec.End < rangeSpec.Start {\n\t\t\treturn nil, fmt.Errorf(\"loop %q: range end (%d) is less than start (%d)\",\n\t\t\t\tstep.ID, rangeSpec.End, rangeSpec.Start)\n\t\t}\n\n\t\t// Expand body for each value in range\n\t\tcount := rangeSpec.End - rangeSpec.Start + 1\n\t\titerNum := 0\n\t\tfor val := rangeSpec.Start; val <= rangeSpec.End; val++ {\n\t\t\titerNum++\n\t\t\t// Build iteration vars: include the loop variable if specified\n\t\t\titerVars := make(map[string]string)\n\t\t\tif step.Loop.Var != \"\" {\n\t\t\t\titerVars[step.Loop.Var] = fmt.Sprintf(\"%d\", val)\n\t\t\t}\n\t\t\titerSteps, err := expandLoopIteration(step, iterNum, iterVars)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tresult = append(result, iterSteps...)","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/formula/controlflow.go#L133-L169","documentation":"After parsing, expandLoopWithVars checks that the range's End is not less than its Start; a descending range would produce a negative iteration count, so expansion fails with the computed bounds in the message. Note this is the vars-expansion path only — validateLoopSpec does not catch this because bounds may come from substituted variables.","triggerScenarios":"A Loop.Range like `10..3`, or variables that resolve so start > end (e.g. `{{start}}..{{end}}` with start=10, end=3), reaching expandLoopWithVars during formula expansion.","commonSituations":"Variables computed in the wrong order (end derived from a smaller value); user input supplying bounds where end < start; hard-coded reversed range typo.","solutions":["Fix the range or the variable values so end >= start","Clamp or swap the values before invoking the formula (e.g. pass max(start,end) as end)","Add pre-expansion validation of resolved variable values in the caller","If descending iteration is intended, it is unsupported — generate an explicit list or separate steps"],"exampleFix":"// before\nvars := map[string]string{\"start\": \"10\", \"end\": \"3\"} // range: \"{{start}}..{{end}}\"\n// after\nvars := map[string]string{\"start\": \"3\", \"end\": \"10\"}","handlingStrategy":"validation","validationCode":"start, _ := strconv.Atoi(vars[\"start\"])\nend, _ := strconv.Atoi(vars[\"end\"])\nif end < start {\n\treturn fmt.Errorf(\"range end (%d) must be >= start (%d)\", end, start)\n}","typeGuard":null,"tryCatchPattern":"expanded, err := expandLoops(steps, vars)\nif err != nil {\n\tif strings.Contains(err.Error(), \"is less than start\") {\n\t\treturn fmt.Errorf(\"swap or clamp range bounds before expansion: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Clamp or swap start/end values before invoking formula expansion","Validate user-supplied range bounds at the input boundary","Remember descending ranges are unsupported"],"tags":["go","formula","range","validation","loops"],"backgroundTag":"invalid-range-bounds","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}