siyuan-note/siyuan · error

createDocTree document title must be a string

Error message

createDocTree document title must be a string

What it means

The title field must be a JSON string. If it is a number, boolean, list, object, or null, the assertion to string fails. The title is used verbatim as a document path segment, so only strings are accepted.

Source

Thrown at kernel/model/template_doc_tree.go:153

		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)
		if "" == title {
			return nil, errors.New("createDocTree document title must not be empty")
		}
		if 512 < utf8.RuneCountInString(title) {
			return nil, fmt.Errorf("createDocTree document title exceeds %d characters", 512)
		}

		templateName, err := templateDocTreeStringField(definition, "template")
		if nil != err {
			return nil, err
		}
		defineName, err := templateDocTreeStringField(definition, "define")
		if nil != err {
			return nil, err
		}
		if "" != templateName && "" != defineName {

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Quote the title value: {"title": "2024"} instead of {"title": 2024}.
  2. In YAML templates, quote numeric-looking titles so the parser reads a string.
  3. Stringify values programmatically (e.g. fmt.Sprintf("%v", v) or JSON stringify) before building the definition.
  4. Pre-validate that typeof/Type-switch on each title is string before calling the API.

Example fix

// before
{"title": 2024}
// after
{"title": "2024"}
Defensive patterns

Strategy: type-guard

Validate before calling

var walk func(nodes []any) error
walk = func(nodes []any) error {
    for _, v := range nodes {
        m, ok := v.(map[string]any)
        if !ok { continue }
        if t, ok := m["title"]; ok {
            if _, isStr := t.(string); !isStr {
                return fmt.Errorf("title must be a string, got %T", t)
            }
        }
        if c, ok := m["children"]; ok {
            if cl, ok := c.([]any); ok {
                if err := walk(cl); err != nil { return err }
            }
        }
    }
    return nil
}

Type guard

func titleIsString(v any) bool {
    m, ok := v.(map[string]any)
    if !ok { return false }
    t, ok := m["title"]
    if !ok { return false }
    _, isStr := t.(string)
    return isStr
}

Try / catch

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

Prevention

When it happens

Trigger: Writing {"title": 42} or {"title": true}; passing a non-string variable into the definition builder; JSON templates where titles from numeric IDs were not stringified.

Common situations: Numeric document names from other systems not quoted; template engines interpolating numbers without conversion; YAML unquoted titles like title: 2024 parsed as integers.

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