{"record":{"id":"dee4ddb828a95b71","repo":"siyuan-note/siyuan","slug":"hpath-refresh-crossed-an-encrypted-notebook-boundary","errorCode":null,"errorMessage":"hpath refresh crossed an encrypted notebook boundary","messagePattern":"hpath refresh crossed an encrypted notebook boundary","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/hpath_refresh.go","lineNumber":407,"sourceCode":"\t\t\treturn\n\t\t}\n\t\tsql.ClearCache()\n\t\tutil.BroadcastByType(\"main\", \"databaseIndexCommit\", 0, \"\", map[string]any{\"rootIDs\": []string{task.ID}, \"backlinkChanged\": true, \"backlinkFull\": true})\n\t\tif !task.started.IsZero() && time.Since(task.started) > time.Second {\n\t\t\tlogging.LogInfof(\"refreshed document hpaths [%s], docs [%d], batches [%d], elapsed [%dms], active [%dms]\", key, len(task.docs), task.batches, time.Since(task.started).Milliseconds(), task.active.Milliseconds())\n\t\t}\n\t}\n}\n\nfunc refreshHPathsTask(task *hpathRefreshTask) (done bool, err error) {\n\tif task.started.IsZero() {\n\t\ttask.started = time.Now()\n\t}\n\troot := treenode.GetBlockTreeInBox(task.ID, task.Box)\n\tif !IsEncryptedBox(task.Box) && (root == nil || root.BoxID != task.Box) {\n\t\troot = treenode.GetBlockTree(task.ID)\n\t\tif root != nil && IsEncryptedBox(root.BoxID) {\n\t\t\treturn false, errors.New(\"hpath refresh crossed an encrypted notebook boundary\")\n\t\t}\n\t}\n\tif root == nil {\n\t\t// 索引尚未恢复时不能丢弃磁盘上仍存在的文档任务。\n\t\tif _, statErr := os.Stat(filepath.Join(util.DataDir, task.Box, task.Path)); statErr == nil {\n\t\t\treturn false, errors.New(\"hpath refresh document is not indexed\")\n\t\t} else if !os.IsNotExist(statErr) {\n\t\t\treturn false, statErr\n\t\t}\n\t\treturn true, nil\n\t}\n\tscope := sha256.Sum256([]byte(root.BoxID + \"\\x00\" + root.Path + \"\\x00\" + root.HPath))\n\tif task.scope != scope {\n\t\ttask.docs, task.index, task.covers = nil, 0, nil\n\t\ttask.blockAfter, task.treeAfter = 0, 0\n\t\ttask.scope = scope\n\t}\n\tif task.docs == nil {","sourceCodeStart":389,"sourceCodeEnd":425,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/hpath_refresh.go#L389-L425","documentation":"refreshHPathsTask resolves the task's root document first from the task's box, and for non-encrypted boxes falls back to the global block tree. If the global lookup finds the document in an encrypted notebook while the task was issued against a non-encrypted box (or vice versa context mismatch), it means the hpath refresh would move data across an encrypted/plain notebook boundary — which is forbidden because the two notebooks use different storage (encryption) formats.","triggerScenarios":"A queued hpath refresh task whose task.Box does not match the block-tree record's BoxID, where the tree record's box is an encrypted notebook while the task box is not (root == nil for task.Box, fallback GetBlockTree finds an encrypted root).","commonSituations":"Block-tree index stale after a notebook was switched to/from encrypted; a task referencing a document that was moved between notebooks; index rebuild race leaving task.Box out of sync.","solutions":["Rebuild the block-tree index so the task's document resolves to the correct box","Verify the notebook encryption status of both task.Box and the tree record's BoxID (IsEncryptedBox) and fix the mismatched flag","Delete the stale hpath refresh task so it is not retried against the wrong notebook","If the document was genuinely moved across notebooks, re-trigger the operation through the normal move API rather than the queued refresh"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if root := treenode.GetBlockTree(id); root != nil && model.IsEncryptedBox(root.BoxID) != model.IsEncryptedBox(taskBox) { /* rebuild index before refreshing */ }","typeGuard":null,"tryCatchPattern":"ok, err := task.refresh(); if err != nil && err.Error() == \"hpath refresh crossed an encrypted notebook boundary\" { // rebuild the block-tree index and clear the stale task }","preventionTips":["Rebuild the block-tree index after changing a notebook's encryption status","Never move documents manually between encrypted and plain notebooks on disk","Keep task queues and index rebuilds serialized"],"tags":["encryption","indexing","hpath","boundary"],"backgroundTag":"invalid-state-transition","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"}