{"record":{"id":"5c25d86030dc8f10","repo":"gastownhall/beads","slug":"inline-expansion-depth-limit-exceeded-max-d-leve","errorCode":null,"errorMessage":"inline expansion depth limit exceeded: max %d levels","messagePattern":"inline expansion depth limit exceeded: max (.+?) levels","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/formula/expand.go","lineNumber":461,"sourceCode":"//\n// This differs from compose.Expand in that the expansion is declared inline on the\n// step itself rather than in a central compose section.\n//\n// Returns a new steps slice with inline expansions applied.\n// The original steps slice is not modified.\nfunc ApplyInlineExpansions(steps []*Step, parser *Parser) ([]*Step, error) {\n\tif parser == nil {\n\t\treturn steps, nil\n\t}\n\n\treturn applyInlineExpansionsRecursive(steps, parser, 0)\n}\n\n// applyInlineExpansionsRecursive handles inline expansions for a slice of steps.\n// depth tracks recursion to prevent infinite expansion loops.\nfunc applyInlineExpansionsRecursive(steps []*Step, parser *Parser, depth int) ([]*Step, error) {\n\tif depth > DefaultMaxExpansionDepth {\n\t\treturn nil, fmt.Errorf(\"inline expansion depth limit exceeded: max %d levels\", DefaultMaxExpansionDepth)\n\t}\n\n\tvar result []*Step\n\n\tfor _, step := range steps {\n\t\t// Check if this step has an inline expansion\n\t\tif step.Expand != \"\" {\n\t\t\t// Load the expansion formula\n\t\t\texpFormula, err := parser.LoadByName(step.Expand)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"inline expand on step %q: loading %q: %w\", step.ID, step.Expand, err)\n\t\t\t}\n\n\t\t\tif expFormula.Type != TypeExpansion {\n\t\t\t\treturn nil, fmt.Errorf(\"inline expand on step %q: %q is not an expansion formula (type=%s)\",\n\t\t\t\t\tstep.ID, step.Expand, expFormula.Type)\n\t\t\t}\n","sourceCodeStart":443,"sourceCodeEnd":479,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/formula/expand.go#L443-L479","documentation":"This error is thrown by applyInlineExpansionsRecursive in internal/formula/expand.go when processing inline step expansions (steps with an Expand field) recurses deeper than DefaultMaxExpansionDepth levels. The library imposes a hard recursion cap so a formula whose expansion references itself (directly or transitively) cannot loop forever. It typically indicates a cycle in the Expand references among expansion formulas.","triggerScenarios":"Calling ApplyInlineExpansions on a steps slice where inline Expand references chain through expansion templates that themselves contain Expand steps, more than DefaultMaxExpansionDepth levels deep — typically because formula A's template expands to formula B, and B back to A.","commonSituations":"Two .formula.toml files accidentally expand each other; copying an Expand line into a template that references its own formula; building deeply nested modular expansions that exceed the fixed depth budget.","solutions":["Inspect the Expand fields along the chain and break the cycle (remove the self/back reference).","Flatten overly deep nesting by inlining some template steps directly instead of chaining Expand references.","If legitimately deep nesting is required, raise DefaultMaxExpansionDepth in the source or refactor into a compose-style expansion.","Log/print the Expand chain per step to identify which formula repeats."],"exampleFix":"// before (mutual cycle)\n// a.formula.toml: [[template]] expand = \"b\"\n// b.formula.toml: [[template]] expand = \"a\"\n// after\n// b.formula.toml: [[template]] id = \"real-step\" (no back-reference to a)","handlingStrategy":"validation","validationCode":"func assertAcyclicExpand(parser *formula.Parser, names []string) error {\n\tseen := map[string]bool{}\n\tvar walk func(name string) error\n\twalk = func(name string) error {\n\t\tif seen[name] {\n\t\t\treturn fmt.Errorf(\"inline expand cycle at %s\", name)\n\t\t}\n\t\tseen[name] = true\n\t\tdefer delete(seen, name)\n\t\tf, err := parser.LoadByName(name)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, s := range f.Template {\n\t\t\tif s.Expand != \"\" {\n\t\t\t\tif err := walk(s.Expand); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\tfor _, n := range names {\n\t\tif err := walk(n); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never let an expansion template reference a formula that (transitively) references it back.","Keep expansion nesting shallow; inline template steps instead of chaining Expand beyond a few levels.","Add a CI test that walks all repo formulas' Expand chains to detect cycles early."],"tags":["go","formula","recursion","inline-expansion"],"backgroundTag":"recursion-depth-limit-exceeded","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}