{"record":{"id":"e8613b532fd9293a","repo":"siyuan-note/siyuan","slug":"createdoctree-document-title-must-be-a-string","errorCode":null,"errorMessage":"createDocTree document title must be a string","messagePattern":"createDocTree document title must be a string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/template_doc_tree.go","lineNumber":153,"sourceCode":"\t\tdefinition, ok := value.(map[string]any)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"createDocTree document must be a dictionary\")\n\t\t}\n\t\tfor key := range definition {\n\t\t\tswitch key {\n\t\t\tcase \"title\", \"template\", \"define\", \"children\":\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"createDocTree document contains unknown field [%s]\", key)\n\t\t\t}\n\t\t}\n\n\t\ttitleValue, ok := definition[\"title\"]\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"createDocTree document title is required\")\n\t\t}\n\t\ttitle, ok := titleValue.(string)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"createDocTree document title must be a string\")\n\t\t}\n\t\ttitle = normalizeDocTitle(title)\n\t\tif \"\" == title {\n\t\t\treturn nil, errors.New(\"createDocTree document title must not be empty\")\n\t\t}\n\t\tif 512 < utf8.RuneCountInString(title) {\n\t\t\treturn nil, fmt.Errorf(\"createDocTree document title exceeds %d characters\", 512)\n\t\t}\n\n\t\ttemplateName, err := templateDocTreeStringField(definition, \"template\")\n\t\tif nil != err {\n\t\t\treturn nil, err\n\t\t}\n\t\tdefineName, err := templateDocTreeStringField(definition, \"define\")\n\t\tif nil != err {\n\t\t\treturn nil, err\n\t\t}\n\t\tif \"\" != templateName && \"\" != defineName {","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/template_doc_tree.go#L135-L171","documentation":"The title field of a createDocTree document must be a JSON string; parseNodes performs a Go type assertion and rejects numbers, booleans, null, objects, or lists. This guarantees the title can be used directly as a document path segment.","triggerScenarios":"Passing a non-string title, e.g. {\"title\": 42}, {\"title\": true}, {\"title\": null}, or {\"title\": [\"Doc\"]} in a createDocTree definition. Raised from parseTemplateDocTreeDefinition during editor template insertion.","commonSituations":"Generating the definition from a script where dates or counters become numbers; templating engines rendering {{date}} as a number; JSON where an empty placeholder was serialized as null.","solutions":["Quote the title so it is a JSON string: {\"title\": \"42\"}.","In the generator, convert the title value to a string before serialization.","Validate the definition with a type check before invoking the template action."],"exampleFix":"// before\n{\"title\": 20260918}\n// after\n{\"title\": \"20260918\"}","handlingStrategy":"type-guard","validationCode":"docs.forEach(d => { if (typeof d.title !== \"string\") d.title = String(d.title); });","typeGuard":"const hasStringTitle = (d) => typeof d.title === 'string';","tryCatchPattern":"try {\n  createDocTree(def);\n} catch (e) {\n  if (String(e).includes(\"title must be a string\")) {\n    def = def.map(d => ({...d, title: String(d.title)}));\n  }\n}","preventionTips":["Coerce dynamic title values with String() before serialization.","Quote all title values in hand-written JSON.","Serialize dates/counters as formatted strings."],"tags":["template","type-error","validation"],"backgroundTag":"type-mismatch","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}