siyuan-note/siyuan · error

createDocTree document must be a dictionary

Error message

createDocTree document must be a dictionary

What it means

Each entry of a createDocTree list must be a JSON/Go map[string]any object describing one document (with fields title/template/define/children). If an element is a string, number, list, or nil, the type assertion to map fails and this error is thrown.

Source

Thrown at kernel/model/template_doc_tree.go:137

}

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)
			}
		}

		titleValue, ok := definition["title"]
		if !ok {
			return nil, errors.New("createDocTree document title is required")
		}
		title, ok := titleValue.(string)
		if !ok {
			return nil, errors.New("createDocTree document title must be a string")
		}
		title = normalizeDocTitle(title)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Convert every list element to an object with at least a title field: ["Doc A"] becomes [{"title": "Doc A"}].
  2. Remove null/empty placeholder entries from the list.
  3. Validate the definition before calling: every element of the top-level list and every children list must be a JSON object.
  4. If titles are stored as strings, map them into objects programmatically before invoking the template action.

Example fix

// before
["Doc A", "Doc B"]
// after
[{"title": "Doc A"}, {"title": "Doc B"}]
Defensive patterns

Strategy: type-guard

Validate before calling

for _, v := range list {
    if _, ok := v.(map[string]any); !ok {
        return fmt.Errorf("every document entry must be an object, got %T", v)
    }
}

Type guard

func isDocObject(v any) bool {
    _, ok := v.(map[string]any)
    return ok
}

Try / catch

nodes, err := parseTemplateDocTreeDefinition(def)
if err != nil {
    if strings.Contains(err.Error(), "must be a dictionary") {
        // convert string titles to {"title": ...} objects and retry
    }
    return err
}

Prevention

When it happens

Trigger: Writing a definition like ["Doc A", "Doc B"] (plain strings); an entry that is null; nesting a bare array as an element; YAML that decoded entries into scalars.

Common situations: Authors assuming titles alone suffice (list of strings); copy-paste errors leaving a stray null or comma-created element; programmatic generation emitting titles instead of objects; schema drift after editing a template by hand.

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/5396663c74ace686. Report an issue: GitHub.