{"record":{"id":"12cecd497b63dbf9","repo":"siyuan-note/siyuan","slug":"conf-language-314-12cecd","errorCode":null,"errorMessage":"Conf.Language(314)","messagePattern":"Conf\\.Language\\(314\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/history_diff.go","lineNumber":326,"sourceCode":"\nfunc readCurrentDocVersionData(blockTree *treenode.BlockTree) (ret []byte, err error) {\n\trelPath, err := filesys.ValidateBoxRelativePath(blockTree.BoxID, blockTree.Path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tencrypted := IsEncryptedBox(blockTree.BoxID)\n\tif encrypted {\n\t\tHoldBoxReadLock(blockTree.BoxID)\n\t\tdefer ReleaseBoxReadLock(blockTree.BoxID)\n\t}\n\tabsPath := filepath.Join(util.DataDir, blockTree.BoxID, filepath.FromSlash(relPath))\n\tret, err = filelock.ReadFile(absPath)\n\tif err != nil || !encrypted {\n\t\treturn\n\t}\n\tdek, err := GetDEKIfUnlocked(blockTree.BoxID)\n\tif err != nil {\n\t\treturn nil, errors.New(Conf.Language(314))\n\t}\n\tret, err = DecryptFile(blockTree.BoxID, relPath, dek, ret)\n\treturn\n}\n\nfunc loadHistoryDocVersion(historyPath string) (ret *loadedDocVersion, err error) {\n\tabsPath, err := validateHistoryPath(historyPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !strings.HasSuffix(strings.ToLower(absPath), \".sy\") {\n\t\treturn nil, errors.New(\"history version is not a document\")\n\t}\n\trelPath, err := filepath.Rel(util.HistoryDir, absPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tparts := strings.SplitN(filepath.ToSlash(relPath), \"/\", 3)","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/history_diff.go#L308-L344","documentation":"When loading the current version of a document belonging to an encrypted notebook, the kernel reads the encrypted .sy file and then calls GetDEKIfUnlocked to obtain the notebook's data-encryption key. If the notebook is encrypted but its key is not currently unlocked (no passphrase supplied / vault locked), it surfaces the localized message keyed 314 (the notebook encryption unlock prompt) instead of proceeding to DecryptFile. This is a deliberate guard so ciphertext is never read without authentication.","triggerScenarios":"Calling any history-diff / doc-version API that resolves the CURRENT document for a notebook with encryption enabled while the notebook's DEK is not unlocked, e.g. GetDEKIfUnlocked(boxID) returns an error because the passphrase was never provided this session or the vault was relocked.","commonSituations":"User enabled notebook encryption but the unlock prompt was dismissed or the key cache expired; a kernel restart cleared unlocked keys; an API caller (plugin/script) hits the diff endpoint before the user unlocked the encrypted notebook; wrong passphrase entered earlier left the notebook locked.","solutions":["Unlock the encrypted notebook by supplying the correct passphrase through the unlock dialog / unlock API so GetDEKIfUnlocked succeeds, then retry the operation.","Verify the notebook actually has encryption enabled (IsEncryptedBox) and that the key envelope is intact; if the key is lost, restore from backup rather than bypassing.","If calling programmatically, check unlock state first and prompt the user to unlock before invoking history/diff endpoints.","Restart flow: re-enter the passphrase after kernel restart; keys are never persisted unlocked."],"exampleFix":"// before: diff API called while notebook locked\nPOST /api/history/convertHistoryDiff doc in encrypted notebook -> Conf.Language(314)\n// after: unlock first\nPOST /api/repo/unlockNotebook { \"box\": \"20240101120000-abc\", \"password\": \"...\" }\n// then retry the history diff request","handlingStrategy":"validation","validationCode":"// Go: check unlock state before requesting the current doc version\nif model.IsEncryptedBox(boxID) {\n    if _, err := model.GetDEKIfUnlocked(boxID); err != nil {\n        return fmt.Errorf(\"notebook %s is locked; unlock it before diffing\", boxID)\n    }\n}","typeGuard":null,"tryCatchPattern":"catch (e) { if (e.msg.includes(Langs[314])) { showNotebookUnlockDialog(box); } else { throw e; } }","preventionTips":["Unlock all encrypted notebooks at session start before invoking history/diff APIs","Check IsEncryptedBox + unlock state before any version operation","Treat kernel restarts as clearing unlocked keys; re-run the unlock flow"],"tags":["encryption","locked-notebook","decryption","history"],"backgroundTag":"authentication-required","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"}