{"record":{"id":"6b7c3f1b085d861d","repo":"siyuan-note/siyuan","slug":"tree-is-empty","errorCode":null,"errorMessage":"tree is empty","messagePattern":"tree is empty","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/filesys/tree.go","lineNumber":225,"sourceCode":"\t\treturn\n\t}\n\n\tret, err = LoadTreeByData(data, boxID, p, luteEngine)\n\tif nil == err {\n\t\tcache.SetTreeDataInBox(rootID, boxID, data)\n\t}\n\treturn\n}\n\nfunc LoadTree(boxID, p string, luteEngine *lute.Lute) (ret *parse.Tree, err error) {\n\tret, _, err = LoadTreeWithFix(boxID, p, luteEngine)\n\treturn\n}\n\n// NormalizeTreeForRead 对只读树应用与文件加载一致的规范化处理，但不写回磁盘。\nfunc NormalizeTreeForRead(tree *parse.Tree) (err error) {\n\tif nil == tree || nil == tree.Root {\n\t\treturn errors.New(\"tree is empty\")\n\t}\n\tif err = treenode.CheckSpec(tree); nil != err {\n\t\treturn\n\t}\n\ttreenode.UpgradeSpec(tree)\n\tescapeAttributeValues(tree)\n\treturn\n}\n\nfunc LoadTreeByData(data []byte, boxID, p string, luteEngine *lute.Lute) (ret *parse.Tree, err error) {\n\tret, err = parseJSON2Tree(boxID, p, data, luteEngine)\n\tif nil != err {\n\t\tlogging.LogErrorf(\"parse tree [%s] failed: %s\", p, err)\n\t\treturn\n\t}\n\tret.Path = p\n\tret.Root.Path = p\n","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/filesys/tree.go#L207-L243","documentation":"Returned by `filesys.NormalizeTreeForRead` when the tree or its `Root` is nil. The function applies read-only normalization (spec check, spec upgrade, attribute escaping) and cannot proceed on an empty tree, so it fails fast rather than nil-dereferencing.","triggerScenarios":"Calling `NormalizeTreeForRead` on a tree that failed to load (nil return) or on a freshly-allocated `parse.Tree` whose `Root` was never set. The check `nil == tree || nil == tree.Root` covers both.","commonSituations":"Chaining a load that returned a nil tree without checking the error; constructing a tree manually for testing and forgetting `Root`; a corrupted file that parsed to an empty tree.","solutions":["Check the tree and error from `LoadTree`/`LoadTreeByData` before passing the tree to `NormalizeTreeForRead`.","If constructing trees in code, always set `tree.Root` before normalizing.","Log and propagate the error so callers do not silently continue with no tree."],"exampleFix":"// before\ntree, _ := filesys.LoadTree(box, p, lute)\nfilesys.NormalizeTreeForRead(tree) // panics on nil\n// after\ntree, err := filesys.LoadTree(box, p, lute)\nif err != nil || tree == nil {\n    return err\n}\nif err := filesys.NormalizeTreeForRead(tree); err != nil {\n    return err\n}","handlingStrategy":"type-guard","validationCode":"// Guard the tree before normalizing:\nif tree == nil || tree.Root == nil {\n    return errors.New(\"tree is empty\")\n}","typeGuard":"func isNonEmptyTree(t *parse.Tree) bool {\n    return t != nil && t.Root != nil\n}","tryCatchPattern":null,"preventionTips":["Always check both the tree pointer and the error from Load functions.","When constructing trees in code, set Root immediately after allocation.","Return early on upstream errors so callers never reach normalize with nil."],"tags":["validation","null-check","tree"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}