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

  1. Flatten the call chain by inlining some defines or removing intermediate wrapper templates
  2. Split the deep hierarchy into several independent doc-tree template invocations
  3. Increase maxTemplateCallDepth in the kernel if deep nesting is intentional, then rebuild
  4. 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

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


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/776025ca4a82aa35. Report an issue: GitHub.