gastownhall/beads · error
applying expansions to %q: %w
Error message
applying expansions to %q: %w
What it means
Thrown when formula.ApplyExpansions fails applying compose-level expansion operators (compose.expand / compose.map) to the resolved steps. It names the formula being cooked and wraps the operator failure, distinguishing compose-operator failures from inline-expansion failures (error 270).
Source
Thrown at cmd/bd/cook.go:756
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 to %q: %w", formulaName, 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)
if err != nil {
return nil, fmt.Errorf("applying expansions to %q: %w", formulaName, err)
}
resolved.Steps = expandedSteps
}
// Apply aspects from compose.aspects
if resolved.Compose != nil && len(resolved.Compose.Aspects) > 0 {
for _, aspectName := range resolved.Compose.Aspects {
aspectFormula, err := parser.LoadByName(aspectName)
if err != nil {
return nil, fmt.Errorf("loading aspect %q: %w", aspectName, err)
}
if aspectFormula.Type != formula.TypeAspect {
return nil, fmt.Errorf("%q is not an aspect formula (type=%s)", aspectName, aspectFormula.Type)
}
if len(aspectFormula.Advice) > 0 {
resolved.Steps = formula.ApplyAdvice(resolved.Steps, aspectFormula.Advice)
}
}View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped cause to see which expand/map operator failed; fix the referenced step ID or template
- Validate the compose block: every expand/map target must match an existing step id
- Define all variables used by compose.map templates in the formula's vars section
- Validate the formula YAML with the parser before cooking (verifyFormula path)
- Re-sync the formula source if the compose block drifted from upstream
Example fix
# before
compose:
expand:
- target: buld # typo, no such step
# after
compose:
expand:
- target: build # matches steps[].id Defensive patterns
Strategy: validation
Validate before calling
// Validate compose operators reference existing step IDs before cooking
stepIDs := map[string]bool{}
for _, s := range f.Steps { stepIDs[s.ID] = true }
if f.Compose != nil {
for _, ex := range f.Compose.Expand {
if !stepIDs[ex.Target] {
return fmt.Errorf("compose.expand target %q not a step id", ex.Target)
}
}
} Try / catch
if err := runCook(); err != nil && strings.Contains(err.Error(), "applying expansions") {
var inner error = errors.Unwrap(err)
log.Printf("compose operator failed: %v", inner) // fix the named expand/map target
} Prevention
- After renaming step IDs, update all compose.expand/compose.map references
- Define every variable used in compose templates in the formula vars section
- Run verifyFormula in CI for formulas with compose blocks
- Keep compose blocks validated whenever merging formula YAML changes
When it happens
Trigger: A formula with a `compose:` block declaring `expand:` or `map:` operators is cooked, and an operator references a nonexistent step ID, an invalid template, or fails variable substitution during expansion. Triggered via bd cook, bd pour, or verifyFormula on that formula.
Common situations: compose.expand references a step id renamed elsewhere in the YAML; compose.map uses a template variable not defined in vars; hand-edited formula YAML left a stale step id in the compose block.
Related errors
- applying expansions: %w
- standalone expansion %q: %w
- branch: from is required
- branch: steps is required
- branch: join is required
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/2ebde6661a7159e5.
Report an issue: GitHub.