gastownhall/beads · error

inline expand on step %q: %w

Error message

inline expand on step %q: %w

What it means

Wrapper error thrown when expandStep fails while instantiating a step's inline expansion template with merged variables. expandStep performs placeholder substitution ({target.title}, variable refs, repeat logic); any failure there (missing required variable, bad placeholder, invalid repeat spec) is re-wrapped with the offending step ID. The root cause is preserved via %w.

Source

Thrown at internal/formula/expand.go:490

				return nil, fmt.Errorf("inline expand on step %q: loading %q: %w", step.ID, step.Expand, err)
			}

			if expFormula.Type != TypeExpansion {
				return nil, fmt.Errorf("inline expand on step %q: %q is not an expansion formula (type=%s)",
					step.ID, step.Expand, expFormula.Type)
			}

			if len(expFormula.Template) == 0 {
				return nil, fmt.Errorf("inline expand on step %q: %q has no template steps", step.ID, step.Expand)
			}

			// Merge formula default vars with step's ExpandVars overrides
			vars := mergeVars(expFormula, step.ExpandVars)

			// Expand the step using the template (reuse existing expandStep)
			expandedSteps, err := expandStep(step, expFormula.Template, 0, vars)
			if err != nil {
				return nil, fmt.Errorf("inline expand on step %q: %w", step.ID, err)
			}

			// Propagate the original step's dependencies to root steps of the expansion
			propagateTargetDeps(step, expandedSteps)

			// Recursively process expanded steps for nested inline expansions
			processedSteps, err := applyInlineExpansionsRecursive(expandedSteps, parser, depth+1)
			if err != nil {
				return nil, err
			}

			result = append(result, processedSteps...)
		} else {
			// No inline expansion - keep the step, but process children recursively
			clone := cloneStep(step)

			if len(step.Children) > 0 {
				processedChildren, err := applyInlineExpansionsRecursive(step.Children, parser, depth)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped inner error to identify the failing placeholder or variable.
  2. Add the missing key to the step's ExpandVars or set it as a default var in the expansion formula.
  3. Fix malformed placeholder syntax in the template (e.g. {target.title} vs a typo like {traget.title}).
  4. Validate the formula/template locally by running it through bd before wiring it into a larger workflow.

Example fix

// before
[[step]]
id = "scaffold"
expand = "scaffolding"
# template needs {project.name} but no vars given
// after
[[step]]
id = "scaffold"
expand = "scaffolding"
[step.expandvars]
project_name = "my-app"
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check that template vars are satisfiable: defaults + ExpandVars cover placeholders used
vars := map[string]string{}
for k, v := range expFormula.Vars {
	vars[k] = v
}
for k, v := range step.ExpandVars {
	vars[k] = v
}
for _, t := range expFormula.Template {
	for _, ph := range placeholderNames(t) { // scan {var.x} tokens
		if _, ok := vars[ph]; !ok {
			return fmt.Errorf("step %q: template needs var %q", step.ID, ph)
		}
	}
}

Try / catch

expanded, err := formula.ApplyInlineExpansions(steps, parser)
if err != nil {
	if strings.Contains(err.Error(), "inline expand on step") {
		return fmt.Errorf("expanding %s: %w", stepID, err) // surface inner cause to user
	}
	return err
}

Prevention

When it happens

Trigger: ApplyInlineExpansions calls expandStep(step, expFormula.Template, 0, vars) and the template references variables not provided by the formula defaults nor the step's ExpandVars, or contains malformed placeholders/repeat directives.

Common situations: Step omits an ExpandVars entry the template requires; placeholder name typo in the template; template uses iteration variables outside a repeat context; formula defaults removed in a newer formula version.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/40f7e1506dc4e25d. Report an issue: GitHub.