siyuan-note/siyuan · error
Encrypted notebooks already exist but the master key backup
Error message
Encrypted notebooks already exist but the master key backup is missing. Restore the original conf.json or backup file to re-enable
What it means
ExportNotebookCryptoBackup fails when the on-disk notebook-crypto backup file does not exist (os.IsNotExist). The function copies the persisted backup (data-crypto-backup.json) into the export directory for user download; if there is no backup file to copy, it returns Conf.Language(315). This signals that encrypted notebooks exist or were expected, but the independent recovery backup is missing — a recovery-required state.
Source
Thrown at kernel/model/crypto.go:282
tmpPath := path + "." + gulu.Rand.String(7) + ".tmp"
if err := os.WriteFile(tmpPath, data, 0644); err != nil {
return err
}
return os.Rename(tmpPath, path)
}
// ExportNotebookCryptoBackup 把密钥备份文件复制到 export 目录,返回可下载的相对路径。
// 供用户主动导出保存,作为同步之外的独立恢复途径(详见设计文档 §4.1)。
// 备份文件本身不含主密码(salt 不保密、verifier 是密文),拿到它也解不开任何数据。
func ExportNotebookCryptoBackup() (downloadPath string, err error) {
notebookCryptoMu.Lock()
defer notebookCryptoMu.Unlock()
backupPath := dataCryptoBackupPath()
data, readErr := filelock.ReadFile(backupPath)
if readErr != nil {
if os.IsNotExist(readErr) {
err = errors.New(Conf.Language(315))
return
}
err = readErr
return
}
exportBase := filepath.Join(util.TempDir, "export")
if mkErr := os.MkdirAll(exportBase, 0755); mkErr != nil {
err = mkErr
return
}
// 用随机名避免不同用户/设备互相覆盖,文件名固定带易识别前缀
fileName := "notebook-crypto-backup-" + gulu.Rand.String(7) + ".json"
downloadPath = "/export/" + url.PathEscape(fileName)
if writeErr := os.WriteFile(filepath.Join(exportBase, fileName), data, 0644); writeErr != nil {
err = writeErr
return
}
returnView on GitHub (pinned to 251596fc0d)
Solutions
- Restore the original conf.json or a previously exported backup file to <DataDir>/.siyuan/data-crypto-backup.json, then retry export.
- If the master password is known and encryption is still enabled in conf.json, regenerate the backup via the re-enable/recovery path rather than exporting a non-existent one.
- Locate the backup from another synced device's DataDir and copy it into place.
Defensive patterns
Strategy: validation
Validate before calling
// Confirm the backup file exists before offering export.
if !filelock.IsExist(filepath.Join(util.DataDir, ".siyuan", "data-crypto-backup.json")) {
// do not offer 'export backup'; route user to recovery instead
} Prevention
- Before offering 'export key backup', check the backup file exists.
- Keep exported backup copies in a separate safe location so a missing on-disk backup is recoverable.
- If the backup is missing while encryption is enabled, treat it as a recovery-required situation and regenerate via the recovery path.
When it happens
Trigger: User invokes 'export key backup' from the encryption settings UI, but <DataDir>/.siyuan/data-crypto-backup.json is absent. Typical when conf.json still references encryption but the backup was manually deleted, an external sync deleted it, or a prior enable/write failed after partial cleanup.
Common situations: User deleted the .siyuan backup file manually; a broken sync removed the backup while leaving encrypted notebook data; conf.json restored from an old copy that points at encryption without a matching backup; filesystem cleanup tools removing hidden files.
Related errors
- Cannot import a key backup while encrypted notebooks are ena
- Decryption failed: incorrect key or corrupted data
- failed to persist key backup: %w
- list encrypted notebooks failed: %w
- check encrypted notebook history failed: %w
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/3dcdb5f31a6bde53.
Report an issue: GitHub.