siyuan-note/siyuan · error
cannot write incomplete notebook crypto backup
Error message
cannot write incomplete notebook crypto backup
What it means
writeNotebookCryptoBackupData refuses to persist a notebook-crypto backup whose configuration is incomplete after stamping the KEKMAC. The library treats a partially initialized NotebookCrypto config as unsafe to back up because restoring from it would leave encryption in a broken state. It throws this sentinel error before any file I/O happens.
Source
Thrown at kernel/model/crypto.go:421
if err != nil {
return fmt.Errorf("marshal notebook crypto backup failed: %w", err)
}
if err := atomicWriteFile(backupPath, data); err != nil {
return fmt.Errorf("write notebook crypto backup failed: %w", err)
}
return nil
}
// writeNotebookCryptoBackupData 将指定的 NotebookCrypto 写入备份文件(不依赖 Conf.NotebookCrypto)。
// kek 必须非 nil:在 Checksum 定型后计算 KEKMAC,保证落盘 MAC 与落盘内容一致。
func writeNotebookCryptoBackupData(nc *conf.NotebookCrypto, kek []byte) error {
if kek == nil {
return errors.New("cannot generate notebook crypto backup without KEK")
}
prepareBackupForWrite(nc)
nc.KEKMAC = computeKEKMAC(nc, kek)
if !notebookCryptoConfigurationComplete(nc) {
return errors.New("cannot write incomplete notebook crypto backup")
}
backupPath := dataCryptoBackupPath()
if err := os.MkdirAll(filepath.Dir(backupPath), 0755); err != nil {
return fmt.Errorf("mkdir notebook crypto backup dir failed: %w", err)
}
data, err := json.Marshal(nc)
if err != nil {
return fmt.Errorf("marshal notebook crypto backup failed: %w", err)
}
if err := atomicWriteFile(backupPath, data); err != nil {
return fmt.Errorf("write notebook crypto backup failed: %w", err)
}
return nil
}
// verifyKEKAgainstExistingBoxes 用 KEK 对所有现有加密笔记本的 WrappedDEK 做无副作用解密校验。
// 优先尝试 conf 的 WrappedDEK,解密失败时 fallback 到 backup(与解锁路径一致);
// GetBoxEncryption 报错时 fail-closed(元数据损坏的加密笔记本不能静默跳过)。View on GitHub (pinned to 8641553a1f)
Solutions
- Inspect the NotebookCrypto config with notebookCryptoConfigurationComplete to find which required field is empty before writing
- Re-import or regenerate the backup from a complete configuration (complete WrappedDEK, salt, KEKMAC)
- Ensure the KEK was correctly derived (deriveKEK succeeded) before attempting backup writes
- Restore the workspace conf from a known-good backup or re-run the master password setup flow
Example fix
// before: writing a partially-populated struct
nc := &conf.NotebookCrypto{Spec: conf.CurrentNotebookCryptoSpec}
err := writeNotebookCryptoBackupData(nc, kek) // fails: incomplete
// after: populate required fields first
nc := &conf.NotebookCrypto{Spec: conf.CurrentNotebookCryptoSpec, WrappedDEK: wrapped, Salt: salt, AutoLock: autoLock}
nc.KEKMAC = computeKEKMAC(nc, kek)
if !notebookCryptoConfigurationComplete(nc) { return errors.New("refusing: incomplete config") }
err := writeNotebookCryptoBackupData(nc, kek) Defensive patterns
Strategy: validation
Validate before calling
if !notebookCryptoConfigurationComplete(nc) {
return fmt.Errorf("backup skipped: incomplete notebook crypto config")
}
if kek == nil {
return fmt.Errorf("backup skipped: KEK not derived")
} Type guard
func backupWritable(nc *conf.NotebookCrypto, kek []byte) bool {
return kek != nil && notebookCryptoConfigurationComplete(nc)
} Prevention
- Always derive the KEK before attempting any backup write
- Run notebookCryptoConfigurationComplete as a precondition in callers, not just inside the writer
- Never persist a NotebookCrypto struct assembled from partial user input
- Validate configs after import/migration before touching the backup path
When it happens
Trigger: Calling ImportNotebookCryptoBackup or triggering tryRestoreNotebookCryptoFromBackupLocked when the resulting conf.NotebookCrypto is missing required fields (e.g. no WrappedDEK, salt, or other mandatory configuration) after prepareBackupForWrite/computeKEKMAC run.
Common situations: A hand-edited or truncated conf.NotebookCrypto JSON, a backup imported from an older/incompatible version missing new spec fields, or enabling notebook encryption where key derivation partially failed leaving nil/empty components.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- unsupported notebook crypto backup spec [%d]
- notebook crypto backup is incomplete or corrupted
- enable encrypted notebook failed: failed to persist key back
- master password migration is pending
- master password migration is pending: Master password change
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/c7eed877501b0f11.
Report an issue: GitHub.