gastownhall/beads · error
applying loops: %w
Error message
applying loops: %w
What it means
ApplyControlFlow wraps any failure from ApplyLoops with 'applying loops: %w'. ApplyLoops expands loop rules (a step repeated over an items list or count) into multiple concrete steps. This error means one of the compose loop rules failed validation or expansion — typically a missing field or a loop target step ID that does not exist in the formula's step list.
Source
Thrown at internal/formula/controlflow.go:558
step.Labels = appendUnique(step.Labels, gateLabel)
}
return nil
}
// ApplyControlFlow applies all control flow operators in the correct order:
// 1. Loops (expand iterations)
// 2. Branches (wire fork-join dependencies)
// 3. Gates (add condition labels)
//
// Returns a new steps slice. The original steps slice is not modified.
func ApplyControlFlow(steps []*Step, compose *ComposeRules) ([]*Step, error) {
var err error
// Apply loops first (expands steps) - ApplyLoops already returns new slice
steps, err = ApplyLoops(steps)
if err != nil {
return nil, fmt.Errorf("applying loops: %w", err)
}
// Build stepMap once for branches and gates
// No need to clone here since ApplyLoops already returned a new slice
stepMap := buildStepMap(steps)
// Apply branches (wires dependencies)
if err := applyBranchesWithMap(stepMap, compose); err != nil {
return nil, fmt.Errorf("applying branches: %w", err)
}
// Apply gates (adds labels)
if err := applyGatesWithMap(stepMap, compose); err != nil {
return nil, fmt.Errorf("applying gates: %w", err)
}
return steps, nil
}View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped inner error (errors.Unwrap or %v of the returned error) — it names the offending loop rule and field
- Check that every compose.Loops[].Target matches an existing step ID in the formula (including nested children)
- Run/inspect the loop rule validation before calling ApplyControlFlow by calling ApplyLoops(steps) directly in a test
- Fix or remove the invalid loop rule in the formula's compose section
Example fix
// before
compose:
loops:
- target: deploy-stp
items: [dev, prod]
// after
compose:
loops:
- target: deploy-step
items: [dev, prod] Defensive patterns
Strategy: validation
Validate before calling
for _, lp := range compose.Loops {
if lp.Target == "" {
return fmt.Errorf("loop rule missing target")
}
found := false
var walk func([]*Step)
walk = func(ss []*Step) {
for _, s := range ss {
if s.ID == lp.Target { found = true }
walk(s.Children)
}
}
walk(steps)
if !found {
return fmt.Errorf("loop target %q not found in steps", lp.Target)
}
}
_, err := ApplyLoops(steps)
return err Try / catch
expanded, err := ApplyControlFlow(steps, compose)
if err != nil {
var inner error = errors.Unwrap(err)
log.Printf("control flow failed: %v (cause: %v)", err, inner)
return fmt.Errorf("check compose loop rules: %w", err)
} Prevention
- Keep step IDs and loop targets in sync; rename via search across the compose section
- Add a unit test that runs ApplyLoops on every formula in the repo
- Validate compose rules at formula-parse time rather than at apply time
- Avoid referencing steps that later control-flow or expansion passes may remove
When it happens
Trigger: Calling ApplyControlFlow(steps, compose) where compose.Loops contains a rule whose Target does not match any step ID, or whose items/count fields are invalid, causing ApplyLoops to return an error.
Common situations: Formula YAML where a loop rule references a step ID with a typo or renamed ID; a loop rule added after the target step was deleted; refactorings that renamed steps without updating the compose loop section.
Related errors
- applying branches: %w
- applying gates: %w
- applying control flow: %w
- applying control flow to %q: %w
- formula %q not accessible: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/a1bb10e5bc537207.
Report an issue: GitHub.