siyuan-note/siyuan · error
template call depth exceeds %d
Error message
template call depth exceeds %d
What it means
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.
Source
Thrown at kernel/model/template_doc_tree_render.go:141
for _, called := range collectCalledTemplateNames(tmpl.Tree.Root) {
calledDepth, err := visit(called)
if nil != err {
return 0, err
}
if depth < calledDepth+1 {
depth = calledDepth + 1
}
}
delete(visiting, name)
depths[name] = depth
return depth, nil
}
depth, err := visit(start)
if nil != err {
return err
}
if maxTemplateCallDepth < depth {
return fmt.Errorf("template call depth exceeds %d", maxTemplateCallDepth)
}
return nil
}
func collectCalledTemplateNames(node templateparse.Node) (ret []string) {
if nil == node {
return nil
}
switch typed := node.(type) {
case *templateparse.ListNode:
if nil == typed {
return nil
}
for _, child := range typed.Nodes {
ret = append(ret, collectCalledTemplateNames(child)...)
}
case *templateparse.TemplateNode:
if nil == typed {View on GitHub (pinned to 8641553a1f)
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
Example fix
// before: a→b→c→d→e... (each adds a level)
{{define "a"}}{{template "b"}}{{end}}
// after: collapse levels with range over data
{{define "a"}}{{range .levels}}{{.}}{{end}}{{end}} Defensive patterns
Strategy: validation
Validate before calling
func callDepth(defines map[string][]string, start string) int {
depths := map[string]int{}
var visit func(string) int
visit = func(n string) int {
if d, ok := depths[n]; ok { return d }
d := 1
for _, c := range defines[n] {
if cd := visit(c) + 1; cd > d { d = cd }
}
depths[n] = d
return d
}
return visit(start)
}
// if callDepth(defines, entry) > maxTemplateCallDepth { ... } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- template document tree output exceeds %d bytes
- inline styles file exceeds the %d byte limit
- attribute view [%s] view [%s] not found
- attribute view [%s] has no available view: %w
- attribute view [%s] visible view [%s] not found
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/776025ca4a82aa35.
Report an issue: GitHub.