gastownhall/beads · error

expand: %q is not an expansion formula (type=%s)

Error message

expand: %q is not an expansion formula (type=%s)

What it means

ApplyExpansions loads the expansion formula named by a compose `expand` rule via parser.LoadByName(rule.With). The formula was found and loaded, but its Type is not TypeExpansion (e.g. it is a workflow, aspect, or convoy formula). The library refuses to expand a step with a non-expansion formula because templates/vars semantics only exist for expansion formulas.

Source

Thrown at internal/formula/expand.go:68

	result := steps
	for _, rule := range compose.Expand {
		targetStep, ok := stepMap[rule.Target]
		if !ok {
			return nil, fmt.Errorf("expand: target step %q not found", rule.Target)
		}

		if expanded[rule.Target] {
			continue // Already expanded
		}

		// Load the expansion formula
		expFormula, err := parser.LoadByName(rule.With)
		if err != nil {
			return nil, fmt.Errorf("expand: loading %q: %w", rule.With, err)
		}

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

		if len(expFormula.Template) == 0 {
			return nil, fmt.Errorf("expand: %q has no template steps", rule.With)
		}

		// Merge formula default vars with rule overrides
		vars := mergeVars(expFormula, rule.Vars)

		// Expand the target step (start at depth 0)
		expandedSteps, err := expandStep(targetStep, expFormula.Template, 0, vars)
		if err != nil {
			return nil, fmt.Errorf("expand %q: %w", rule.Target, err)
		}

		// Propagate target step's dependencies to root steps of the expansion.
		// Root steps are those whose needs/dependsOn only reference IDs within
		// the expansion (or are empty) — they are the entry points.

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the referenced formula's `type:` field in its definition file and change it to `expansion`
  2. Point the rule's `with:` at the correct expansion formula name
  3. If the formula should remain a workflow, remove/replace the expand rule instead of expanding with it

Example fix

# before (.formula)
with: build-workflow   # type: workflow
# after
with: build-expand     # type: expansion
Defensive patterns

Strategy: validation

Validate before calling

f, err := parser.LoadByName(rule.With)
if err != nil { return err }
if f.Type != formula.TypeExpansion {
    return fmt.Errorf("rule %q: formula %q must be type expansion, got %s", rule.Target, rule.With, f.Type)
}

Type guard

func isExpansionFormula(f *formula.Formula) bool { return f != nil && f.Type == formula.TypeExpansion }

Try / catch

expanded, err := formula.ApplyExpansions(steps, compose)
if err != nil {
    var notExp *formula.ErrWrongType // or strings.Contains check
    if strings.Contains(err.Error(), "is not an expansion formula") {
        log.Fatalf("fix `with:` in compose: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling ApplyExpansions with a compose whose Expand rules reference a `with:` formula that exists but is of type workflow/aspect/convoy instead of `expansion`.

Common situations: Typo or copy-paste pointing `with:` at a regular workflow formula; a formula was converted from expansion to another type; reuse of an existing formula name assuming it was an expansion.

Related errors


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