{"record":{"id":"8a13ba8893cd7cfe","repo":"siyuan-note/siyuan","slug":"indexing","errorCode":null,"errorMessage":"indexing","messagePattern":"indexing","errorType":"error_code","errorClass":"ErrIndexing","httpStatus":null,"severity":"warning","filePath":"kernel/model/tree.go","lineNumber":206,"sourceCode":"\t\t\tlogging.LogErrorf(\"decrypt tree [path=%s] failed: %s\", localPath, err)\n\t\t\treturn\n\t\t}\n\t}\n\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 {\n\t\t// boxID 未知时（如通用打开入口），遍历所有已打开的加密笔记本查找","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/tree.go#L188-L224","documentation":"Sentinel error ErrIndexing declared at tree.go:206 ('indexing'). Returned when a block/tree lookup fails because the kernel is actively indexing (task.ContainIndexTask() is true) OR the searchTreeLimiter rate limiter prevents a filesystem fallback reindex (indexTreeInFilesystem at tree.go:332). It signals a TRANSIENT state: the data may become available once indexing completes.","triggerScenarios":"During boot/notebook reindex, task.ContainIndexTask() returns true so LoadTreeByBlockIDWithReindexInBox/loadTreeByBlockIDInBox0 return ErrIndexing instead of attempting filesystem reindex; alternatively, the rate limiter (1 per 3s) blocks a fallback reindex attempt. API handlers in block.go check errors.Is(err, model.ErrIndexing) to return a 'try again later' response.","commonSituations":"Opening/operating on a doc immediately after kernel boot while notebooks are still indexing; a large notebook reindex in progress; rapid repeated lookups triggering the rate limiter; importing a large dataset then immediately querying.","solutions":["Treat ErrIndexing as transient: retry the operation after indexing completes (poll boot progress or use the indexing-complete WebSocket event).","Use errors.Is(err, model.ErrIndexing) to return a 503/retry-after style response to API clients.","Avoid flooding lookups during reindex; the rate limiter (3s) intentionally throttles filesystem reindex attempts.","If stuck, check task.ContainIndexTask() status and kernel boot progress (util.IsBooted())."],"exampleFix":"// before — treat all errors as fatal\ntree, err := LoadTreeByBlockID(id)\nif err != nil { return fatal(err) }\n\n// after — retry on transient indexing error\ntree, err := LoadTreeByBlockID(id)\nif errors.Is(err, model.ErrIndexing) {\n    return retryLater()\n}","handlingStrategy":"retry","validationCode":"func isStillIndexing() bool {\n    return task.ContainIndexTask() || !util.IsBooted()\n}","typeGuard":null,"tryCatchPattern":"for attempt := 0; attempt < maxAttempts; attempt++ {\n    tree, err := model.LoadTreeByBlockID(id)\n    if err == nil { return tree, nil }\n    if !errors.Is(err, model.ErrIndexing) { return nil, err }\n    // wait for indexing progress via WS event, then retry\n    waitForIndexProgress()\n}","preventionTips":["Treat ErrIndexing as transient — retry after indexing completes, do not surface as a hard failure.","Subscribe to the boot-progress / indexing-complete WebSocket event to know when to retry.","Avoid hammering lookups during reindex; the 3s rate limiter will throttle filesystem reindex attempts."],"tags":["indexing","sentinel","transient","retry","boot"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}