gastownhall/beads · error
applying control flow: %w
Error message
applying control flow: %w
What it means
cmd/bd/cook.go:176 wraps errors from formula.ApplyControlFlow(resolved.Steps, resolved.Compose), which evaluates control-flow operators (loops, branches, gates) declared in the formula into a flat step list. The formula resolved cleanly, but a control-flow construct is invalid: e.g. a loop with no iterable/bad range expression, a branch with missing condition or missing target steps, or a gate referencing an undefined step.
Source
Thrown at cmd/bd/cook.go:176
f, err := parser.LoadByName(formulaPath)
if err != nil {
// Fall back to parsing as a file path
f, err = parser.ParseFile(formulaPath)
if err != nil {
return nil, fmt.Errorf("parsing formula: %w", err)
}
}
// Resolve inheritance
resolved, err := parser.Resolve(f)
if err != nil {
return nil, fmt.Errorf("resolving formula: %w", err)
}
// Apply control flow operators - loops, branches, gates
controlFlowSteps, err := formula.ApplyControlFlow(resolved.Steps, resolved.Compose)
if err != nil {
return nil, fmt.Errorf("applying control flow: %w", err)
}
resolved.Steps = controlFlowSteps
// Apply advice transformations
if len(resolved.Advice) > 0 {
resolved.Steps = formula.ApplyAdvice(resolved.Steps, resolved.Advice)
}
// Apply inline step expansions
inlineExpandedSteps, err := formula.ApplyInlineExpansions(resolved.Steps, parser)
if err != nil {
return nil, fmt.Errorf("applying inline expansions: %w", err)
}
resolved.Steps = inlineExpandedSteps
// Apply expansion operators
if resolved.Compose != nil && (len(resolved.Compose.Expand) > 0 || len(resolved.Compose.Map) > 0) {
expandedSteps, err := formula.ApplyExpansions(resolved.Steps, resolved.Compose, parser)View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped error to identify the offending operator (loop/branch/gate) and step id, then fix that construct in the formula file.
- Verify every branch/gate target references an existing step id in the same formula.
- Validate loop expressions: correct variable name, non-empty collection, correct syntax for the range/foreach form.
- Remove or comment out the control-flow block and re-run to isolate which construct is malformed.
Example fix
// before (formula step) branch: on-fail -> step: "verify-fix" # step id renamed // after branch: on-fail -> step: "verify" # matches existing step id
Defensive patterns
Strategy: validation
Validate before calling
func controlFlowTargetsExist(steps []*formula.Step) error {
ids := map[string]bool{}
for _, s := range steps {
ids[s.ID] = true
}
for _, s := range steps {
for _, target := range s.ControlFlowTargets() { // branch/gate step references
if !ids[target] {
return fmt.Errorf("step %q references unknown step %q", s.ID, target)
}
}
}
return nil
} Type guard
func stepExists(steps []*formula.Step, id string) bool {
for _, s := range steps {
if s.ID == id {
return true
}
}
return false
} Try / catch
steps, err := formula.ApplyControlFlow(resolved.Steps, resolved.Compose)
if err != nil {
return fmt.Errorf("bad loop/branch/gate in formula %q: %w", formulaName, err)
} Prevention
- After renaming or deleting a step, update every branch/gate that targets its id.
- Keep loop collection expressions simple and test them with a two-item list before generalizing.
- Validate formulas in CI by running ApplyControlFlow on each registry entry.
- Provide all variables a gate condition reads at cook time; avoid optional gate vars.
When it happens
Trigger: A formula step uses control-flow syntax (foreach/loop, branch/if, gate) and ApplyControlFlow cannot evaluate it: empty or malformed loop collection expression, branch condition that doesn't parse, branch/gate target step id that doesn't exist in the step list.
Common situations: Hand-written loop expressions with wrong variable syntax; a branch referencing a step renamed or removed during editing; a gate condition using a variable not provided to the run; copy-pasting control-flow blocks between formulas without adjusting step ids.
Related errors
- applying control flow to %q: %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/9ac4b870d465a6a1.
Report an issue: GitHub.