gastownhall/beads · error

materializing expansion %q: %w

Error message

materializing expansion %q: %w

What it means

MaterializeExpansion builds concrete steps for an expansion formula by calling expandStep with depth 0 and the merged vars. If expandStep fails (depth limit or substitution error), the error is wrapped with the formula name for context.

Source

Thrown at internal/formula/expand.go:433

// This enables expansion formulas to be directly instantiated via wisp/pour
// without requiring a Compose wrapper (bd-qzb).
//
// No-op if the formula is not an expansion type, has no Template, or already
// has Steps.
func MaterializeExpansion(f *Formula, targetID string, vars map[string]string) error {
	if f.Type != TypeExpansion || len(f.Template) == 0 || len(f.Steps) > 0 {
		return nil
	}

	target := &Step{
		ID:          targetID,
		Title:       f.Formula,
		Description: f.Description,
	}

	expandedSteps, err := expandStep(target, f.Template, 0, vars)
	if err != nil {
		return fmt.Errorf("materializing expansion %q: %w", f.Formula, err)
	}

	f.Steps = expandedSteps
	return nil
}

// ApplyInlineExpansions applies Step.Expand fields to inline expansions.
// Steps with the Expand field set are replaced by the referenced expansion template.
// The step's ExpandVars are passed as variable overrides to the expansion.
//
// This differs from compose.Expand in that the expansion is declared inline on the
// step itself rather than in a central compose section.
//
// Returns a new steps slice with inline expansions applied.
// The original steps slice is not modified.
func ApplyInlineExpansions(steps []*Step, parser *Parser) ([]*Step, error) {
	if parser == nil {
		return steps, nil

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped inner error for the root cause
  2. Define all template variables as defaults in the expansion formula or pass them via vars
  3. Remove recursive inline expansions from the template

Example fix

# before: uses ${env} but env undefined
steps:
  - id: "run-${env}"
# after
vars:
  env: dev
steps:
  - id: "run-${env}"
Defensive patterns

Strategy: try-catch

Validate before calling

for _, v := range requiredVars(f.Template) { // scan {var} placeholders
    if _, ok := vars[v]; !ok { return fmt.Errorf("missing var %q for formula %q", v, f.Formula) }
}

Type guard

func hasAllVars(f *formula.Formula, vars map[string]string) bool {
    for _, name := range templateVarNames(f.Template) {
        if _, ok := vars[name]; !ok { return false }
    }
    return true
}

Try / catch

if err := formula.MaterializeExpansion(f); err != nil {
    if strings.Contains(err.Error(), "depth limit") {
        return fmt.Errorf("formula %s expansion too deep: %w", f.Formula, err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling MaterializeExpansion on a formula whose template causes expandStep to fail — e.g. template placeholders referencing variables absent from f.Vars, or (via inline expansion) recursion pushing depth past 5.

Common situations: Materializing a formula with missing variable defaults; templates that inline-expand into themselves; partially edited templates with broken {var} references.

Related errors


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