{"record":{"id":"c2b1328bead784e5","repo":"gastownhall/beads","slug":"map-q-has-no-template-steps","errorCode":null,"errorMessage":"map: %q has no template steps","messagePattern":"map: %q has no template steps","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/formula/expand.go","lineNumber":117,"sourceCode":"\n\t\t// Rebuild stepMap from result so subsequent iterations see resolved deps\n\t\tstepMap = buildStepMap(result)\n\t}\n\n\t// Apply map rules (pattern matching)\n\tfor _, rule := range compose.Map {\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(\"map: loading %q: %w\", rule.With, err)\n\t\t}\n\n\t\tif expFormula.Type != TypeExpansion {\n\t\t\treturn nil, fmt.Errorf(\"map: %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(\"map: %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// Find all matching steps (including nested children)\n\t\t// Rebuild stepMap to capture any changes from previous expansions\n\t\tstepMap = buildStepMap(result)\n\t\tvar toExpand []*Step\n\t\tfor id, step := range stepMap {\n\t\t\tif MatchGlob(rule.Select, id) && !expanded[id] {\n\t\t\t\ttoExpand = append(toExpand, step)\n\t\t\t}\n\t\t}\n\n\t\t// Expand each matching step\n\t\tfor _, targetStep := range toExpand {\n\t\t\texpandedSteps, err := expandStep(targetStep, expFormula.Template, 0, vars)","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/formula/expand.go#L99-L135","documentation":"The map rule's `with:` formula is an expansion formula but defines no template steps. Like the expand variant, map cannot replace matched steps with an empty expansion, so it errors.","triggerScenarios":"ApplyExpansions validates each map rule's loaded formula and finds len(expFormula.Template) == 0.","commonSituations":"Expansion formula file with type: expansion but an empty/missing steps block; template steps removed during refactoring; malformed template key so parsing yields zero steps.","solutions":["Add template steps to the expansion formula","Restore accidentally deleted template steps","Fix the template key/format so steps parse correctly"],"exampleFix":"# before\nname: fanout\ntype: expansion\nsteps: []\n# after\nname: fanout\ntype: expansion\nsteps:\n  - id: \"${name}-child\"\n    title: \"Child of ${name}\"","handlingStrategy":"validation","validationCode":"f, err := parser.LoadByName(rule.With)\nif err == nil && len(f.Template) == 0 {\n    return fmt.Errorf(\"map rule with=%q: expansion formula 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(\"empty expansion formula referenced by map rule: %w\", err)\n}","preventionTips":["Lint that every expansion formula defines >=1 template step","Review formula refactors for deleted steps","Fail fast on empty templates in a build-time schema check"],"tags":["formula","map","template"],"backgroundTag":"empty-expansion-template","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}