siyuan-note/siyuan · error

createDocTree exceeds the maximum depth of %d

Error message

createDocTree exceeds the maximum depth of %d

What it means

createDocTree's recursive definition parser (parseNodes) rejects a definition whose nesting exceeds maxTemplateDocTreeDepth (16 levels). The depth counter starts at 1 for the top-level list and increments for each nested "children" list. The limit protects the kernel from stack exhaustion and runaway recursive templates when a document-tree template is rendered.

Source

Thrown at kernel/model/template_doc_tree.go:123

func parseTemplateDocTreeDefinition(def any) ([]*TemplateDocTreeNode, error) {
	state := &templateDocTreeParseState{}
	nodes, err := state.parseNodes(def, 1)
	if nil != err {
		return nil, err
	}
	if 0 == len(nodes) {
		return nil, errors.New("createDocTree requires at least one document")
	}
	return nodes, nil
}

type templateDocTreeParseState struct {
	count int
}

func (state *templateDocTreeParseState) parseNodes(value any, depth int) ([]*TemplateDocTreeNode, error) {
	if maxTemplateDocTreeDepth < depth {
		return nil, fmt.Errorf("createDocTree exceeds the maximum depth of %d", maxTemplateDocTreeDepth)
	}
	values, ok := value.([]any)
	if !ok {
		return nil, errors.New("createDocTree definition must be a list")
	}
	if 0 == len(values) {
		return nil, errors.New("createDocTree document list must not be empty")
	}

	nodes := make([]*TemplateDocTreeNode, 0, len(values))
	for _, value := range values {
		definition, ok := value.(map[string]any)
		if !ok {
			return nil, errors.New("createDocTree document must be a dictionary")
		}
		for key := range definition {
			switch key {
			case "title", "template", "define", "children":

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Flatten the definition so no document is more than 16 levels below the root; move deep levels into sibling branches or separate templates.
  2. Check the definition data programmatically before rendering: compute the maximum children nesting depth and reject/refactor anything above 15 nested levels (root list = level 1).
  3. If the nesting comes from generated data, break cycles in the source data or serialize cyclic references instead of recursive children arrays.
  4. If the limit genuinely blocks a legitimate use, split the tree creation into multiple createDocTree calls targeting different parent documents.

Example fix

// before: 17-level nested children
{"title": "l0", "children": [{"title": "l1", "children": [ ... l2..l16 ... ]}]}
// after: split into shallower trees
{"title": "l0", "children": [{"title": "l1", "children": [{"title": "l2"}]}]}
// keep nesting <= 16 levels total
Defensive patterns

Strategy: validation

Validate before calling

func maxDepth(def any) int {
    list, ok := def.([]any)
    if !ok || len(list) == 0 {
        return 1
    }
    d := 1
    for _, v := range list {
        m, ok := v.(map[string]any)
        if !ok { continue }
        if c, ok := m["children"]; ok {
            if sub := maxDepth(c) + 1; sub > d { d = sub }
        }
    }
    return d
}
// call only if maxDepth(def) <= 16

Type guard

func isDocList(v any) bool { _, ok := v.([]any); return ok }

Try / catch

nodes, err := parseTemplateDocTreeDefinition(def)
if err != nil {
    if strings.Contains(err.Error(), "exceeds the maximum depth") {
        // flatten or split the definition, surface a user-facing message
    }
    return err
}

Prevention

When it happens

Trigger: Calling parseTemplateDocTreeDefinition (via the createDocTree template action) with a definition containing more than 16 levels of nested children arrays, e.g. a self-nesting or accidentally duplicated children structure that nests 17+ deep.

Common situations: A template author writes deeply nested children lists by hand; a generator or script produces recursive nesting from cyclic data; an LLM generates an over-nested doc-tree template; a previous version allowed more depth and an old template now exceeds the new cap.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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