{"record":{"id":"c71c2730a2248bcd","repo":"siyuan-note/siyuan","slug":"list-encrypted-notebooks-failed-w","errorCode":null,"errorMessage":"list encrypted notebooks failed: %w","messagePattern":"list encrypted notebooks failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto.go","lineNumber":991,"sourceCode":"// KEK 不缓存——启用后用户需对每个加密笔记本单独调 UnlockBox 解锁。\nfunc EnableEncryptedNotebook(password string) error {\n\tif len(password) == 0 {\n\t\treturn errors.New(\"password must not be empty\")\n\t}\n\n\tnotebookCryptoMu.Lock()\n\tdefer notebookCryptoMu.Unlock()\n\n\tConf.m.RLock()\n\tcurrent := *Conf.NotebookCrypto\n\tConf.m.RUnlock()\n\tif current.Enabled && notebookCryptoConfigurationComplete(&current) {\n\t\treturn errors.New(Conf.Language(312))\n\t}\n\n\thasEncrypted, listErr := hasEncryptedNotebook()\n\tif listErr != nil {\n\t\treturn fmt.Errorf(\"list encrypted notebooks failed: %w\", listErr)\n\t}\n\thasHistory, historyErr := scanEncryptedNotebookHistory()\n\tif historyErr != nil {\n\t\treturn fmt.Errorf(\"check encrypted notebook history failed: %w\", historyErr)\n\t}\n\thasBackup := filelock.IsExist(dataCryptoBackupPath())\n\tif hasEncrypted || hasHistory || hasBackup {\n\t\t// 现存笔记本、已删除笔记本历史或全局备份均表示已有密钥域，必须恢复并认证，不能生成新 MasterSalt。\n\t\tkek, restoreErr := tryRestoreNotebookCryptoFromBackupLocked(password)\n\t\tif kek != nil {\n\t\t\tzeroAndClear(kek)\n\t\t}\n\t\tif restoreErr != nil {\n\t\t\tif strings.Contains(restoreErr.Error(), Conf.Language(311)) {\n\t\t\t\treturn errors.New(Conf.Language(311))\n\t\t\t}\n\t\t\treturn errors.New(Conf.Language(315))\n\t\t}","sourceCodeStart":973,"sourceCodeEnd":1009,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/crypto.go#L973-L1009","documentation":"EnableEncryptedNotebook returns a wrapped error at line 991 when hasEncryptedNotebook() fails. This is a precondition scan used to decide whether to recover an existing key domain or generate a fresh MasterSalt; if the scan itself errors, the function cannot safely proceed in either direction and aborts. The underlying error is preserved via %w.","triggerScenarios":"hasEncryptedNotebook() returns a non-nil error while listing/scanning notebooks for encryption markers. Causes: notebook index/blocktree read failure, filesystem error reading notebook metadata, or a corrupted notebook directory.","commonSituations":"Workspace with a corrupted .sy metadata file; blocktree.db locked or unreadable; notebook directory permission error; concurrent unmount/mount racing with the scan; storage media degradation.","solutions":["Inspect the wrapped error (errors.Unwrap) to identify whether it is blocktree, filesystem, or notebook-metadata related.","Resolve the underlying storage/index issue (repair permissions, close competing locks, reindex) and retry.","Do not attempt to bypass the scan by clearing state — that risks generating a new key domain over existing encrypted data."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Unwrap and route the precondition failure.\nif err := model.EnableEncryptedNotebook(password); err != nil {\n    var unwrapped error\n    if errors.As(err, &unwrapped) {\n        log.Printf(\"notebook scan failed: %v\", unwrapped)\n    }\n}","preventionTips":["Resolve notebook-index/storage issues before attempting to enable encryption.","Do not bypass the scan by clearing state — doing so risks orphaning existing encrypted notebooks.","Treat scan failure as fail-closed; retry only after the underlying issue is fixed."],"tags":["crypto","encryption","notebook","filesystem","precondition"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}