{"record":{"id":"49a1565d6960f1a2","repo":"siyuan-note/siyuan","slug":"enable-encrypted-notebook-failed-failed-to-persis","errorCode":null,"errorMessage":"enable encrypted notebook failed: failed to persist key backup: %w","messagePattern":"enable encrypted notebook failed: failed to persist key backup: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto.go","lineNumber":1077,"sourceCode":"\t}\n\n\tConf.m.Lock()\n\tprevious := *Conf.NotebookCrypto\n\tConf.NotebookCrypto.Enabled = true\n\tConf.NotebookCrypto.MasterSalt = salt\n\tConf.NotebookCrypto.KDFParams = params\n\tConf.NotebookCrypto.KEKVerifier = verifierCT\n\tConf.NotebookCrypto.VerifierNonce = verifierNonce\n\tConf.m.Unlock()\n\n\t// 先持久化恢复备份，再提交 conf。此时尚无加密笔记本和历史依赖，任一步失败都不会孤立既有密文。\n\tif err := saveNotebookCryptoBackup(kek); err != nil {\n\t\t// 备份写失败则恢复启用前的内存配置；conf 尚未写入，无需再执行磁盘回滚。\n\t\tlogging.LogErrorf(\"save notebook crypto backup failed: %s\", err)\n\t\tConf.m.Lock()\n\t\t*Conf.NotebookCrypto = previous\n\t\tConf.m.Unlock()\n\t\treturn fmt.Errorf(\"enable encrypted notebook failed: failed to persist key backup: %w\", err)\n\t}\n\t// Conf.Save 内部会加 Conf.m，不能在持锁状态下调用（RWMutex 不可重入）。\n\t// 即使配置写入失败，已落盘的备份仍可在下次启动时恢复同一套密钥材料。\n\tConf.Save()\n\tIncSync()\n\treturn nil\n}\n\n// DisableEncryptedNotebook 关闭加密笔记本功能。前置：不能有加密笔记本存在，\n// 且不能有依赖当前密钥备份的已删除笔记本历史（否则禁用并删除备份会让这些历史永久锁死，违反 §19）。\n// 清除全局加密配置（MasterSalt/KEKVerifier），KEK/DEK 不再可用。\nfunc DisableEncryptedNotebook() error {\n\tnotebookCryptoMu.Lock()\n\tdefer notebookCryptoMu.Unlock()\n\n\t// 检查是否还有加密笔记本（含 conf 损坏但存在备份的）\n\tids, listErr := listAllEncryptedBoxIDs()\n\tif listErr != nil {","sourceCodeStart":1059,"sourceCodeEnd":1095,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/crypto.go#L1059-L1095","documentation":"EnableEncryptedNotebook persists the key backup (saveNotebookCryptoBackup) BEFORE committing the new crypto configuration to conf.json. If writing that backup fails, it rolls back the in-memory NotebookCrypto settings to their previous value and returns \"enable encrypted notebook failed: failed to persist key backup\" wrapping the underlying write error, so the feature is never left half-enabled with keys that only exist in volatile conf.","triggerScenarios":"Calling EnableEncryptedNotebook when saveNotebookCryptoBackup cannot write the backup file: workspace data directory read-only, disk full, filesystem/permission errors, antivirus locking the file, or filelock contention on the backup path.","commonSituations":"Running SiYuan from a read-only mount or full disk; workspace moved to a directory the kernel user cannot write; permission changes after an OS update; backup path locked by a backup/sync tool; container with a read-only /data volume.","solutions":["Check the wrapped underlying error for the actual filesystem cause (permission denied, no space left, etc.) and fix disk space or permissions on the workspace data directory","Ensure the SiYuan process user owns/can write to the workspace and its data subdirectory (chmod/chown)","Retry the enable call once the filesystem is writable; the operation is idempotent when no key domain exists yet","If a stale/partial backup file blocks the write, remove it manually — only safe when no encrypted notebooks exist yet"],"exampleFix":"// before\nerr := model.EnableEncryptedNotebook(password)\n// after\nif err := model.EnableEncryptedNotebook(password); err != nil {\n    if strings.Contains(err.Error(), \"failed to persist key backup\") {\n        // inspect fs permissions / free space on workspacePath/data before retrying\n    }\n}","handlingStrategy":"try-catch","validationCode":"// Pre-check writability of the backup target's directory before enabling\nif err := os.MkdirAll(filepath.Dir(model.DataCryptoBackupPath()), 0o755); err != nil { /* abort: cannot write workspace data dir */ }\nif err := filelock.WriteFile(model.DataCryptoBackupPath()+\".wtest\", []byte(\"ok\")); err != nil { /* abort */ }","typeGuard":"func isBackupPersistFailure(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"failed to persist key backup\")\n}","tryCatchPattern":"if err := model.EnableEncryptedNotebook(pwd); err != nil {\n    if isBackupPersistFailure(err) { /* fix fs perms/space reported by wrapped cause, then retry */ }\n}","preventionTips":["Ensure the workspace data directory is writable by the kernel process user","Monitor free disk space in containers/CI before enabling encryption","Exclude the workspace from antivirus/backup tools that lock files","Mount volumes read-write, not read-only"],"tags":["filesystem","file-write","encryption","disk"],"backgroundTag":"file-write-failed","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"}