{"record":{"id":"0c197d8daa32d9f8","repo":"gastownhall/beads","slug":"expand-q-has-no-template-steps","errorCode":null,"errorMessage":"expand: %q has no template steps","messagePattern":"expand: %q has no template steps","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/formula/expand.go","lineNumber":72,"sourceCode":"\t\t\treturn nil, fmt.Errorf(\"expand: target step %q not found\", rule.Target)\n\t\t}\n\n\t\tif expanded[rule.Target] {\n\t\t\tcontinue // Already expanded\n\t\t}\n\n\t\t// Load the expansion formula\n\t\texpFormula, err := parser.LoadByName(rule.With)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"expand: loading %q: %w\", rule.With, err)\n\t\t}\n\n\t\tif expFormula.Type != TypeExpansion {\n\t\t\treturn nil, fmt.Errorf(\"expand: %q is not an expansion formula (type=%s)\", rule.With, expFormula.Type)\n\t\t}\n\n\t\tif len(expFormula.Template) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"expand: %q has no template steps\", rule.With)\n\t\t}\n\n\t\t// Merge formula default vars with rule overrides\n\t\tvars := mergeVars(expFormula, rule.Vars)\n\n\t\t// Expand the target step (start at depth 0)\n\t\texpandedSteps, err := expandStep(targetStep, expFormula.Template, 0, vars)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"expand %q: %w\", rule.Target, err)\n\t\t}\n\n\t\t// Propagate target step's dependencies to root steps of the expansion.\n\t\t// Root steps are those whose needs/dependsOn only reference IDs within\n\t\t// the expansion (or are empty) — they are the entry points.\n\t\tpropagateTargetDeps(targetStep, expandedSteps)\n\n\t\t// Replace the target step with expanded steps\n\t\tresult = replaceStep(result, rule.Target, expandedSteps)","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/formula/expand.go#L54-L90","documentation":"The `with:` formula in an expand rule is a valid expansion formula, but its template has zero steps. An expansion with no template steps cannot produce any steps, so ApplyExpansions rejects it rather than silently deleting the target step.","triggerScenarios":"ApplyExpansions encounters an expand rule whose formula passes the TypeExpansion check but has an empty Template slice (no steps defined in the formula file).","commonSituations":"Formula file defines type: expansion but no steps/template section; steps were commented out or removed during editing; a template YAML/JSON key was misspelled so it parsed to an empty list.","solutions":["Add template steps to the referenced expansion formula","Restore accidentally removed/commented template steps","Fix the misspelled template key so the steps parse into the formula"],"exampleFix":"# before\nname: matrix-expand\ntype: expansion\n# steps missing\n# after\nname: matrix-expand\ntype: expansion\nsteps:\n  - id: \"item-${item}\"\n    title: \"Item ${item}\"","handlingStrategy":"validation","validationCode":"f, _ := parser.LoadByName(rule.With)\nif f.Type == formula.TypeExpansion && len(f.Template) == 0 {\n    return fmt.Errorf(\"expansion formula %q has no template steps\", rule.With)\n}","typeGuard":"func hasTemplate(f *formula.Formula) bool { return f != nil && f.Type == formula.TypeExpansion && len(f.Template) > 0 }","tryCatchPattern":"expanded, err := formula.ApplyExpansions(steps, compose)\nif err != nil && strings.Contains(err.Error(), \"has no template steps\") {\n    return fmt.Errorf(\"expansion formula %s is empty: %w\", rule.With, err)\n}","preventionTips":["Every `type: expansion` formula must define at least one template step — lint for it","Don't leave steps commented out in committed formulas","Parse formulas in CI to catch empty templates early"],"tags":["formula","expansion","template"],"backgroundTag":"empty-expansion-template","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}