siyuan-note/siyuan · error
failed to persist key backup: %w
Error message
failed to persist key backup: %w
What it means
ImportNotebookCryptoBackup returns a wrapped error at line 358 when writeNotebookCryptoBackupData fails while persisting the validated backup to <DataDir>/.siyuan/data-crypto-backup.json. By this point every cryptographic check passed and the in-memory nc was prepared; the failure is purely I/O (mkdir, marshal, or atomic write). The error wraps the underlying cause with %w so callers can inspect it.
Source
Thrown at kernel/model/crypto.go:358
if !verifyKEKMAC(nc, kek) {
return errors.New(Conf.Language(317))
}
decrypted, dErr := util.DecryptWithAAD(kek, nc.KEKVerifier, []byte("siyuan:kek-verifier"))
if dErr != nil || string(decrypted) != string(kekVerifierMagic) {
return errors.New(Conf.Language(311)) // 主密码错误
}
// 校验 KEK 能解密现存笔记本和已删除笔记本历史中的 WrappedDEK,避免导入不匹配的备份。
if !verifyKEKAgainstExistingBoxes(kek) || !verifyKEKAgainstEncryptedHistory(kek) {
return errors.New(Conf.Language(316)) // 密钥不匹配
}
nc.KDFParams = params // 确保写回 Conf 的参数已经通过完整校验。
nc.Enabled = true
// 先写 backup,再提交 conf;backup 失败时 conf 尚未改变,可重试
if err := writeNotebookCryptoBackupData(nc, kek); err != nil {
return fmt.Errorf("failed to persist key backup: %w", err)
}
Conf.m.Lock()
*Conf.NotebookCrypto = *nc
Conf.m.Unlock()
Conf.Save()
IncSync()
return nil
}
// saveNotebookCryptoBackup 把当前 NotebookCrypto(含 MasterSalt/KEKVerifier/KDFParams)备份到 DataDir。
// kek 必须非 nil:在 Checksum 定型后计算 KEKMAC 并落盘,保证恢复路径可通过 MAC 校验。
// 无 KEK 生成的备份 KEKMAC 必为空,会被 deriveKEK/恢复路径拒绝,等于制造无法解锁的状态(详见设计 §19)。
func saveNotebookCryptoBackup(kek []byte) error {
if kek == nil {
// 无 KEK 时不得生成当前格式备份:KEKMAC 缺失会被 deriveKEK/恢复路径拒绝,
// 生成即等于制造无法解锁的状态。
return errors.New("cannot generate notebook crypto backup without KEK")
}View on GitHub (pinned to 251596fc0d)
Solutions
- Inspect the wrapped underlying error (errors.Unwrap / errors.Is) to identify mkdir vs write vs marshal.
- Free disk space and verify write permissions on <DataDir>/.siyuan/.
- Retry the import once the filesystem issue is resolved — conf has not been mutated yet at this point (the comment notes conf is unchanged on backup failure, so retry is safe).
Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure DataDir is writable before importing.
dir := filepath.Join(util.DataDir, ".siyuan")
if info, err := os.Stat(dir); err != nil || !info.IsDir() {
return errors.New("data dir not available")
} Try / catch
// Unwrap to distinguish mkdir vs write vs marshal failures.
if err := model.ImportNotebookCryptoBackup(data, password); err != nil {
var inner error
if errors.As(err, &inner) {
log.Printf("persist failed: %v", inner)
}
} Prevention
- Verify write permission and free space on <DataDir>/.siyuan before importing.
- Retry is safe: on persist failure conf has not been mutated yet.
- Inspect the wrapped error to localize mkdir vs atomic-write vs marshal.
When it happens
Trigger: writeNotebookCryptoBackupData returns an error: the backup directory cannot be created (os.MkdirAll fails), the JSON marshal fails, or atomicWriteFile fails. Causes include insufficient permissions on DataDir, read-only filesystem, disk full, or path resolution failure.
Common situations: DataDir on a read-only or full volume; permission/ownership change on the workspace; antivirus/lock preventing the temp write; removable media ejected mid-operation.
Related errors
- save generated image failed: %w
- Encrypted notebooks already exist but the master key backup
- write data [%s] failed: %s
- save OAuth client registration: %w
- save OAuth credentials: %w
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/fbc1a06019402733.
Report an issue: GitHub.