gastownhall/beads · error
applying branches: %w
Error message
applying branches: %w
What it means
ApplyControlFlow wraps failures from applyBranchesWithMap with 'applying branches: %w'. Branch rules wire fork-join dependency patterns: all branch steps depend on the 'from' step and the 'join' step depends on all branch steps. This error means a branch rule is missing a required field (from, steps, join) or references a step ID that does not exist in the step map.
Source
Thrown at internal/formula/controlflow.go:567
// 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
}
// cloneStepDeep creates a deep copy of a step including children.
func cloneStepDeep(s *Step) *Step {
clone := cloneStep(s)
if len(s.Children) > 0 {
clone.Children = make([]*Step, len(s.Children))
for i, child := range s.Children {
clone.Children[i] = cloneStepDeep(child)View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped inner error — it says exactly which field is missing or which step ID was not found
- Verify every branch From, Join, and Steps ID exists in the formula after loop expansion (expansion can rename/replace steps)
- Run ApplyLoops first and inspect the expanded step IDs to confirm branch targets survive expansion
- Correct the branch rule IDs or remove the stale rule in the compose section
Example fix
// before
branches:
- from: plan
steps: [build-api, build-web]
join: intergrate
// after
branches:
- from: plan
steps: [build-api, build-web]
join: integrate Defensive patterns
Strategy: validation
Validate before calling
for _, b := range compose.Branch {
if b.From == "" || b.Join == "" || len(b.Steps) == 0 {
return fmt.Errorf("branch rule incomplete: from=%q join=%q steps=%d", b.From, b.Join, len(b.Steps))
}
ids := buildStepMap(steps) // includes nested children
for _, id := range append([]string{b.From, b.Join}, b.Steps...) {
if _, ok := ids[id]; !ok {
return fmt.Errorf("branch references missing step %q", id)
}
}
} Try / catch
out, err := ApplyControlFlow(steps, compose)
if err != nil {
if strings.Contains(err.Error(), "applying branches") {
return fmt.Errorf("invalid branch rule in compose: %w", err)
}
return err
} Prevention
- Validate branch from/join/steps IDs against the formula's step list before applying
- Run loop expansion first when authoring, since branch targets may be expansion products
- Test every formula's compose rules with ApplyBranches in CI
- Delete stale branch rules when removing steps
When it happens
Trigger: Calling ApplyControlFlow(steps, compose) where a compose.Branch entry has empty From/Join/Steps, or where From, Join, or any Steps entry names a step ID absent from the formula (checked via stepMap built after loop expansion).
Common situations: Typos in branch from/join/step IDs in formula compose config; a branch rule pointing at a step created or renamed by a loop expansion under different IDs; deleting a step without updating the branch rule.
Related errors
- applying loops: %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/5ca1f2154c04ce77.
Report an issue: GitHub.