{"record":{"id":"efdb81e505eab0b4","repo":"siyuan-note/siyuan","slug":"createdoctree-document-s-must-be-a-string","errorCode":null,"errorMessage":"createDocTree document %s must be a string","messagePattern":"createDocTree document (.+?) must be a string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/template_doc_tree.go","lineNumber":207,"sourceCode":"\t\t\tchildren, parseErr := state.parseNodes(childrenValue, depth+1)\n\t\t\tif nil != parseErr {\n\t\t\t\treturn nil, parseErr\n\t\t\t}\n\t\t\tnode.Children = children\n\t\t}\n\t\tnodes = append(nodes, node)\n\t}\n\treturn nodes, nil\n}\n\nfunc templateDocTreeStringField(definition map[string]any, key string) (string, error) {\n\tvalue, exists := definition[key]\n\tif !exists {\n\t\treturn \"\", nil\n\t}\n\tret, ok := value.(string)\n\tif !ok {\n\t\treturn \"\", fmt.Errorf(\"createDocTree document %s must be a string\", key)\n\t}\n\tret = strings.TrimSpace(ret)\n\tif \"\" == ret {\n\t\treturn \"\", fmt.Errorf(\"createDocTree document %s must not be empty\", key)\n\t}\n\treturn ret, nil\n}\n\nfunc (collector *templateDocTreeCollector) create(def any) (string, error) {\n\tif !collector.enabled || !collector.allowCreation {\n\t\treturn \"\", errors.New(\"createDocTree is only available when manually inserting a template in the editor\")\n\t}\n\tnodes, err := parseTemplateDocTreeDefinition(def)\n\tif nil != err {\n\t\treturn \"\", err\n\t}\n\tif maxTemplateDocTreeDocs < len(flattenTemplateDocTreeNodes0(collector.nodes))+len(flattenTemplateDocTreeNodes0(nodes)) {\n\t\treturn \"\", fmt.Errorf(\"createDocTree exceeds the maximum document count of %d\", maxTemplateDocTreeDocs)","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/template_doc_tree.go#L189-L225","documentation":"templateDocTreeStringField validates the optional \"template\" or \"define\" field of a createDocTree document entry. If the key exists but its value is not a JSON string (e.g. a number, boolean, object, or array), parsing fails with \"createDocTree document <key> must be a string\". The parser is strict because both fields are later resolved to template file names.","triggerScenarios":"A createDocTree document entry like {\"title\":\"x\",\"template\":123}, {\"title\":\"x\",\"define\":true}, or {\"template\":{...}} passed to the collector's create() or rendered via parseTemplateDocTreeDefinition.","commonSituations":"Hand-writing template syntax with an unquoted value, JSON produced by a script using non-string types, or copying a doc entry where the field was set programmatically to a number/null.","solutions":["Quote the value: change \"template\": 123 to \"template\": \"123\".","Remove the field entirely if no child template/define is needed (absent keys are allowed).","If generated by code, coerce the value to a string before building the definition (e.g. str(value))."],"exampleFix":"// before\n{\"title\":\"Child\",\"template\":42}\n// after\n{\"title\":\"Child\",\"template\":\"child-template\"}","handlingStrategy":"validation","validationCode":"for (const doc of definition) {\n  for (const key of [\"template\", \"define\"]) {\n    if (key in doc && typeof doc[key] !== \"string\") throw new Error(`${key} must be a string`);\n  }\n}","typeGuard":"const isNonEmptyString = (v) => typeof v === \"string\" && v.trim().length > 0;","tryCatchPattern":"try {\n  render(def);\n} catch (e) {\n  if (/must be a string/.test(String(e))) {\n    // fix the offending field type in the definition\n  }\n}","preventionTips":["Always quote template/define values in template syntax","Validate generated JSON types before calling the render pipeline","Omit optional fields instead of passing non-string placeholders"],"tags":["template","type-mismatch","validation"],"backgroundTag":"type-mismatch","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}