{"record":{"id":"21a9c7e085614346","repo":"siyuan-note/siyuan","slug":"cannot-stage-an-empty-tree","errorCode":null,"errorMessage":"cannot stage an empty tree","messagePattern":"cannot stage an empty tree","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/import_obsidian.go","lineNumber":2339,"sourceCode":"\t\tnode.SetIALAttr(\"updated\", util.TimeFromID(node.ID))\n\t\treturn ast.WalkContinue\n\t})\n\ttree.ID = doc.ID\n\ttree.Box = \"\"\n\ttree.Path = doc.TargetPath\n\ttree.HPath = doc.HPath\n\ttree.Root.Box = \"\"\n\ttree.Root.Path = doc.TargetPath\n\ttree.Root.Spec = treenode.CurrentSpec\n\ttree.Root.SetIALAttr(\"id\", doc.ID)\n\ttree.Root.SetIALAttr(\"title\", doc.Title)\n\ttree.Root.SetIALAttr(\"updated\", util.TimeFromID(doc.ID))\n\ttree.Root.RemoveIALAttrsByPrefix(\"custom-\")\n}\n\nfunc writeObsidianTempTree(docsTemp string, tree *parse.Tree) error {\n\tif tree == nil || tree.Root == nil {\n\t\treturn errors.New(\"cannot stage an empty tree\")\n\t}\n\tif tree.Root.FirstChild == nil {\n\t\ttree.Root.AppendChild(treenode.NewParagraph(\"\"))\n\t}\n\ttreenode.UpgradeSpec(tree)\n\ttree.Root.SetIALAttr(\"type\", \"doc\")\n\tluteEngine := util.NewLute()\n\trenderer := render.NewJSONRenderer(tree, luteEngine.RenderOptions, luteEngine.ParseOptions)\n\tdata := renderer.Render()\n\tif !json.Valid(data) {\n\t\treturn fmt.Errorf(\"generated tree [%s] is not valid JSON\", tree.HPath)\n\t}\n\tif !util.UseSingleLineSave {\n\t\tvar buffer bytes.Buffer\n\t\tif err := json.Indent(&buffer, data, \"\", \"\\t\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdata = buffer.Bytes()","sourceCodeStart":2321,"sourceCodeEnd":2357,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/import_obsidian.go#L2321-L2357","documentation":"Before writing a converted document tree into the temporary staging directory, writeObsidianTempTree rejects a nil tree or a tree without a Root node. Such a tree could not be serialized into a valid .sy document, so staging fails fast with this error.","triggerScenarios":"Calling writeObsidianTempTree(docsTemp, tree) where the transform step previously returned nil (parse failure propagated as a nil *parse.Tree) or produced a Tree whose Root field is nil.","commonSituations":"A parse failure earlier in the pipeline was ignored (nil tree returned without error), leading to this downstream error instead of the original parse failure; a code path constructing a Tree literal forgot to assign Root.","solutions":["Fix the upstream transform so a nil tree surfaces as a proper parse error (error 347) instead of being passed to writeObsidianTempTree","Check why the tree for that document has no Root — usually a parse.Parse nil result that was swallowed","Re-run the import after fixing the problematic source note","Add an explicit nil-tree check in the caller to report the document name before staging"],"exampleFix":"// before: nil tree silently passed on\ntree, stats := transformObsidianMarkdown(vault, doc, data)\nerr := writeObsidianTempTree(docsTemp, tree)\n// after: fail fast with document context\nif tree == nil || tree.Root == nil {\n\treturn fmt.Errorf(\"transform produced no tree for doc [%s]\", doc.ID)\n}\nerr := writeObsidianTempTree(docsTemp, tree)","handlingStrategy":"validation","validationCode":"if tree == nil || tree.Root == nil {\n\treturn fmt.Errorf(\"transform produced no tree for doc [%s]\", doc.ID)\n}","typeGuard":"func hasUsableRoot(t *parse.Tree) bool { return t != nil && t.Root != nil }","tryCatchPattern":"if err := writeObsidianTempTree(docsTemp, tree); err != nil && strings.Contains(err.Error(), \"empty tree\") {\n\t// find the upstream parse/transform failure that returned a nil tree\n}","preventionTips":["Always check the transform step's error before using its tree","Fail fast with document context instead of passing nil trees downstream","Never swallow nil-tree returns from parse.Parse"],"tags":["staging","parse-tree","obsidian-import","nil-check"],"backgroundTag":"empty-required-field","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}