{"record":{"id":"3fcf864ac23968db","repo":"siyuan-note/siyuan","slug":"parse-tree-failed-import-obsidian","errorCode":null,"errorMessage":"parse tree failed","messagePattern":"parse tree failed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/import_obsidian.go","lineNumber":2213,"sourceCode":"\t\t\t\tret[index] = ' '\n\t\t\t}\n\t\t}\n\t}\n\treturn ret\n}\n\nfunc parseObsidianMd(markdown []byte, vault *obsidianVaultContext, doc *obsidianDocPlan, stats *obsidianTransformStats) (*parse.Tree, error) {\n\tluteEngine := util.NewStdLute()\n\tluteEngine.SetYamlFrontMatter(true)\n\tluteEngine.SetBlockRef(true)\n\tluteEngine.SetKramdownIAL(true)\n\tluteEngine.SetCallout(true)\n\tluteEngine.SetArbitraryTaskListItemMarker(true)\n\tluteEngine.SetExportNormalizeTaskListMarker(false)\n\tluteEngine.SetSup(true)\n\ttree := parse.Parse(\"\", markdown, luteEngine.ParseOptions)\n\tif tree == nil {\n\t\treturn nil, errors.New(\"parse tree failed\")\n\t}\n\tnormalizeTree(tree)\n\tnormalizeObsidianCallouts(tree)\n\thtmlBlock2Inline(tree)\n\tparse.TextMarks2Inlines(tree)\n\tparse.NestedInlines2FlattedSpansHybrid(tree, false)\n\tmarkObsidianFootnoteRefs(tree, stats)\n\trewriteObsidianMarkdownLinks(tree, vault, doc, stats)\n\tassignObsidianTreeIDs(tree, doc, stats.ReservedIDs)\n\treturn tree, nil\n}\n\nfunc markObsidianFootnoteRefs(tree *parse.Tree, stats *obsidianTransformStats) {\n\tif len(stats.FootnoteIDs) == 0 {\n\t\treturn\n\t}\n\tast.Walk(tree.Root, func(node *ast.Node, entering bool) ast.WalkStatus {\n\t\tif entering && treenode.IsBlockRef(node) && stats.FootnoteIDs[node.TextMarkBlockRefID] {","sourceCodeStart":2195,"sourceCodeEnd":2231,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/import_obsidian.go#L2195-L2231","documentation":"During Obsidian markdown conversion the kernel parses the document with the Lute engine into a syntax tree. If `parse.Parse` returns nil the conversion cannot continue, so it fails with 'parse tree failed'. This is an internal guard against a degenerate parse result.","triggerScenarios":"Calling the conversion path (which configures callouts, sup, arbitrary task-list markers, then `parse.Parse(\"\", markdown, luteEngine.ParseOptions)`) when the parser returns a nil tree for the given markdown bytes.","commonSituations":"Malformed or empty Obsidian markdown input; extremely unusual constructs that defeat the parser; a Lute engine version producing nil for edge-case input.","solutions":["Inspect the source markdown document for corruption or emptiness and repair it","Log/mark the failing note and continue the import for remaining documents","Update Lute / the kernel, then retry the import"],"exampleFix":"// before: hard failure on nil tree\ntree := parse.Parse(\"\", markdown, luteEngine.ParseOptions)\nif tree == nil {\n    return nil, errors.New(\"parse tree failed\")\n}\n// after: fall back to a minimal tree so the note still imports\ntree := parse.Parse(\"\", markdown, luteEngine.ParseOptions)\nif tree == nil {\n    stats.AddSkipped(\"parse failed\")\n    return nil, stats\n}","handlingStrategy":"try-catch","validationCode":"if len(bytes.TrimSpace(markdown)) == 0 {\n    return errors.New(\"empty markdown input\")\n}","typeGuard":null,"tryCatchPattern":"tree, err := convertMarkdown(markdown)\nif err != nil {\n    log.Printf(\"skipping document: %v\", err)\n    return nil\n}","preventionTips":["Repair corrupt Obsidian notes before import","Keep Lute and the kernel in sync","Log failing documents and continue rather than aborting the whole import"],"tags":["parser","lute","obsidian-import"],"backgroundTag":"unexpected-response-shape","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"}