gastownhall/beads · error

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

Error message

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

What it means

Same check as the expand-rule variant but for map rules: the formula loaded by a map rule's `with:` is not of type expansion. Map rules require expansion formulas because they substitute each matching step into a template.

Source

Thrown at internal/formula/expand.go:113

		if len(expandedSteps) > 0 {
			lastStepID := expandedSteps[len(expandedSteps)-1].ID
			result = UpdateDependenciesForExpansion(result, rule.Target, lastStepID)
		}

		// Rebuild stepMap from result so subsequent iterations see resolved deps
		stepMap = buildStepMap(result)
	}

	// Apply map rules (pattern matching)
	for _, rule := range compose.Map {
		// Load the expansion formula
		expFormula, err := parser.LoadByName(rule.With)
		if err != nil {
			return nil, fmt.Errorf("map: loading %q: %w", rule.With, err)
		}

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

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

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

		// Find all matching steps (including nested children)
		// Rebuild stepMap to capture any changes from previous expansions
		stepMap = buildStepMap(result)
		var toExpand []*Step
		for id, step := range stepMap {
			if MatchGlob(rule.Select, id) && !expanded[id] {
				toExpand = append(toExpand, step)
			}
		}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Set the referenced formula's type to `expansion`
  2. Update the map rule's `with:` to name an actual expansion formula
  3. Remove the map rule if expansion is not intended

Example fix

# before
with: deploy-aspect   # type: aspect
# after
with: deploy-expand   # type: expansion
Defensive patterns

Strategy: validation

Validate before calling

f, err := parser.LoadByName(rule.With)
if err == nil && f.Type != formula.TypeExpansion {
    return fmt.Errorf("map rule with=%q must reference an expansion formula, got %s", rule.With, f.Type)
}

Type guard

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

Try / catch

expanded, err := formula.ApplyExpansions(steps, compose)
if err != nil && strings.Contains(err.Error(), "is not an expansion formula") {
    return fmt.Errorf("map rule points at wrong formula type: %w", err)
}

Prevention

When it happens

Trigger: ApplyExpansions loads a map rule's `with:` formula successfully, but expFormula.Type is workflow/aspect/convoy instead of TypeExpansion.

Common situations: Pointing a map rule at a workflow formula by mistake; a formula's type was changed after the compose referenced it; copying a `with:` value from another compose section.

Related errors


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