siyuan-note/siyuan · error

createDocTree definition must be a list

Error message

createDocTree definition must be a list

What it means

parseNodes expects the value passed to it (the createDocTree definition, or a node's "children" value) to be a JSON/Go []any list. If a string, map, number, or nil is supplied instead, the type assertion fails and this error is thrown. It is a structural schema check on the template's document-tree declaration.

Source

Thrown at kernel/model/template_doc_tree.go:127

		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":
			default:
				return nil, fmt.Errorf("createDocTree document contains unknown field [%s]", key)
			}
		}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Wrap the definition in a list: the top-level argument and every children value must be an array of document objects.
  2. Change a single child object children: {title: x} into children: [{title: x}].
  3. Verify the parsed type before calling: ensure json.Unmarshal/YAML decode yields []any (slice) at every list position, not map[string]any or string.
  4. If the definition comes from user input, validate it with a schema validator that enforces array-of-objects at these positions.

Example fix

// before
{"title": "Parent", "children": {"title": "Child"}}
// after
{"title": "Parent", "children": [{"title": "Child"}]}
Defensive patterns

Strategy: type-guard

Validate before calling

if _, ok := def.([]any); !ok {
    return fmt.Errorf("definition must be an array")
}
for _, v := range def.([]any) {
    m, ok := v.(map[string]any)
    if !ok { continue }
    if c, ok := m["children"]; ok {
        if _, ok := c.([]any); !ok {
            return fmt.Errorf("children must be an array")
        }
    }
}

Type guard

func isDocDefinition(v any) bool {
    _, isList := v.([]any)
    return isList
}

Try / catch

nodes, err := parseTemplateDocTreeDefinition(def)
if err != nil {
    if strings.Contains(err.Error(), "must be a list") {
        // wrap the value in []any{...} and retry, or report a schema error
    }
    return err
}

Prevention

When it happens

Trigger: Passing a single object (map) instead of an array as the createDocTree definition; writing children as a single object or a comma-separated string instead of an array; passing the YAML-decoded definition where scalars were expected to be lists.

Common situations: Hand-written template JSON where the outermost array is omitted; a children field written as {"title": ...} instead of [{"title": ...}]; templates migrated from another format that used an object keyed by title; data that YAML/JSON parsed into a scalar for empty/ambiguous values.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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