{"record":{"id":"776025ca4a82aa35","repo":"siyuan-note/siyuan","slug":"template-call-depth-exceeds-d","errorCode":null,"errorMessage":"template call depth exceeds %d","messagePattern":"template call depth exceeds (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/template_doc_tree_render.go","lineNumber":141,"sourceCode":"\t\tfor _, called := range collectCalledTemplateNames(tmpl.Tree.Root) {\n\t\t\tcalledDepth, err := visit(called)\n\t\t\tif nil != err {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t\tif depth < calledDepth+1 {\n\t\t\t\tdepth = calledDepth + 1\n\t\t\t}\n\t\t}\n\t\tdelete(visiting, name)\n\t\tdepths[name] = depth\n\t\treturn depth, nil\n\t}\n\tdepth, err := visit(start)\n\tif nil != err {\n\t\treturn err\n\t}\n\tif maxTemplateCallDepth < depth {\n\t\treturn fmt.Errorf(\"template call depth exceeds %d\", maxTemplateCallDepth)\n\t}\n\treturn nil\n}\n\nfunc collectCalledTemplateNames(node templateparse.Node) (ret []string) {\n\tif nil == node {\n\t\treturn nil\n\t}\n\tswitch typed := node.(type) {\n\tcase *templateparse.ListNode:\n\t\tif nil == typed {\n\t\t\treturn nil\n\t\t}\n\t\tfor _, child := range typed.Nodes {\n\t\t\tret = append(ret, collectCalledTemplateNames(child)...)\n\t\t}\n\tcase *templateparse.TemplateNode:\n\t\tif nil == typed {","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/template_doc_tree_render.go#L123-L159","documentation":"validateTemplateCallGraph computes the maximum static nesting depth of {{template}} calls starting from the entry template. If the computed depth exceeds maxTemplateCallDepth, rendering is rejected with this error — a guard against pathological (though non-recursive) template nesting that could blow the stack or produce enormous output.","triggerScenarios":"A chain of defines each calling the next (A→B→C→… deeper than maxTemplateCallDepth), or many nested child-template files in a doc tree each including further templates, so the longest call chain exceeds the configured maximum.","commonSituations":"Layered template packages built as wrappers-upon-wrappers; generated templates that chain includes per heading level; combining multiple template packages that each add nesting layers.","solutions":["Flatten the call chain by inlining some defines or removing intermediate wrapper templates","Split the deep hierarchy into several independent doc-tree template invocations","Increase maxTemplateCallDepth in the kernel if deep nesting is intentional, then rebuild","Refactor repeated per-level templates into a single range-driven template where possible"],"exampleFix":"// before: a→b→c→d→e... (each adds a level)\n{{define \"a\"}}{{template \"b\"}}{{end}}\n// after: collapse levels with range over data\n{{define \"a\"}}{{range .levels}}{{.}}{{end}}{{end}}","handlingStrategy":"validation","validationCode":"func callDepth(defines map[string][]string, start string) int {\n\tdepths := map[string]int{}\n\tvar visit func(string) int\n\tvisit = func(n string) int {\n\t\tif d, ok := depths[n]; ok { return d }\n\t\td := 1\n\t\tfor _, c := range defines[n] {\n\t\t\tif cd := visit(c) + 1; cd > d { d = cd }\n\t\t}\n\t\tdepths[n] = d\n\t\treturn d\n\t}\n\treturn visit(start)\n}\n// if callDepth(defines, entry) > maxTemplateCallDepth { ... }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep template nesting shallow; prefer data-driven range loops over stacked wrapper defines","Measure include-chain length when combining multiple template packages","Inline single-use wrapper defines to shorten the call chain"],"tags":["go","template","depth","limits"],"backgroundTag":"template-call-depth-exceeded","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}