{"record":{"id":"4562081f91cefdb5","repo":"siyuan-note/siyuan","slug":"errtreenotfound","errorCode":"ErrTreeNotFound","errorMessage":"tree not found","messagePattern":"tree not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/tree.go","lineNumber":213,"sourceCode":"\t\t}\n\t}\n\n\tif err = treenode.CheckSpecJSON(data); nil != err {\n\t\treturn\n\t}\n\tret, err = dataparser.ParseJSONWithoutFix(data, luteEngine.ParseOptions)\n\tif err != nil {\n\t\tlogging.LogErrorf(\"parse json to tree [%s] failed: %s\", localPath, err)\n\t\treturn\n\t}\n\treturn\n}\n\nvar (\n\tErrBoxNotFound   = errors.New(\"notebook not found\")\n\tErrBoxClosed     = errors.New(\"notebook closed\")\n\tErrBlockNotFound = errors.New(\"block not found\")\n\tErrTreeNotFound  = errors.New(\"tree not found\")\n\tErrIndexing      = errors.New(\"indexing\")\n\tErrBoxUnindexed  = errors.New(\"notebook unindexed\")\n\tErrInvalidID     = errors.New(\"invalid id\")\n)\n\nfunc LoadTreeByBlockIDWithReindex(id string) (ret *parse.Tree, err error) {\n\treturn LoadTreeByBlockIDWithReindexInBox(id, \"\")\n}\n\n// LoadTreeByBlockIDWithReindexInBox 与 LoadTreeByBlockIDWithReindex 一致，但按 boxID 路由 blocktree 查询。\nfunc LoadTreeByBlockIDWithReindexInBox(id, boxID string) (ret *parse.Tree, err error) {\n\tif \"\" == id {\n\t\tlogging.LogWarnf(\"block id is empty\")\n\t\treturn nil, ErrTreeNotFound\n\t}\n\n\tbt := treenode.GetBlockTreeInBox(id, boxID)\n\tif nil == bt && \"\" == boxID {","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/tree.go#L195-L231","documentation":"ErrTreeNotFound is the sentinel error meaning the document tree for a given ID could not be loaded. Doc-info and path-lookup functions (getDocInfo, getBlockInfo, getHPathByID, getFullHPathByID, export prep) return it when no tree matches the ID. API handlers deliberately swallow it in some endpoints because 'not found' should degrade quietly rather than surface as a message.","triggerScenarios":"getDocInfo/getBlockInfo called with an ID absent from the index; getHPathByID/getPathByID/getFullHPathByID with a deleted doc's ID; prepareExportAssets encountering a missing tree during export.","commonSituations":"Client or plugin requesting a document deleted on another device before sync removed it locally; corrupted blocktree index missing the tree; links/bookmarks pointing at removed documents.","solutions":["Confirm the document still exists (/api/filetree/getHPathByID) and update or remove stale references to the deleted doc","Rebuild the notebook index if the .sy file exists but the in-memory/blocktree index lost it","Handle errors.Is(err, model.ErrTreeNotFound) as an expected 'missing' case in API callers (as block.go:710 does) instead of reporting an error message"],"exampleFix":"// before\nret.Msg = err.Error()\n// after\nif err != nil && !errors.Is(err, model.ErrTreeNotFound) {\n    ret.Msg = err.Error()\n}","handlingStrategy":"try-catch","validationCode":"hp, err := getHPathByID(id)\nif err != nil || hp == \"\" { return fmt.Errorf(\"doc %s not found\", id) }","typeGuard":null,"tryCatchPattern":"if errors.Is(err, model.ErrTreeNotFound) {\n    // treat as expected missing-doc case; degrade quietly\n    return nil\n}","preventionTips":["Confirm documents exist after sync before referencing their IDs","Rebuild the index if lookups fail for docs that exist on disk","Treat missing trees as a normal outcome in plugins, not a crash"],"tags":["go","tree","document","not-found"],"backgroundTag":"record-not-found","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"}