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

  1. Inspect the NotebookCrypto config with notebookCryptoConfigurationComplete to find which required field is empty before writing
  2. Re-import or regenerate the backup from a complete configuration (complete WrappedDEK, salt, KEKMAC)
  3. Ensure the KEK was correctly derived (deriveKEK succeeded) before attempting backup writes
  4. 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

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


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/c7eed877501b0f11. Report an issue: GitHub.