siyuan-note/siyuan · error

cannot generate notebook crypto backup without KEK

Error message

cannot generate notebook crypto backup without KEK

What it means

saveNotebookCryptoBackup nil-guard: it was invoked with a nil KEK. A backup written without a KEK has an empty KEKMAC and would be rejected by deriveKEK/recovery paths — creating an unrecoverable state — so generation is refused outright (design §19).

Source

Thrown at kernel/model/crypto.go:382

	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")
	}
	Conf.m.Lock()
	nc := *Conf.NotebookCrypto // 值拷贝
	prepareBackupForWrite(&nc)
	nc.KEKMAC = computeKEKMAC(&nc, kek)
	if !notebookCryptoConfigurationComplete(&nc) {
		Conf.m.Unlock()
		return errors.New("cannot save incomplete notebook crypto configuration")
	}
	Conf.NotebookCrypto.Spec = nc.Spec
	Conf.NotebookCrypto.BackupID = nc.BackupID
	Conf.NotebookCrypto.CreatedAt = nc.CreatedAt
	Conf.NotebookCrypto.Checksum = nc.Checksum
	Conf.NotebookCrypto.KEKMAC = nc.KEKMAC // 保持 Conf 与备份文件的 KEKMAC 一致
	Conf.m.Unlock()
	backupPath := dataCryptoBackupPath()
	if err := os.MkdirAll(filepath.Dir(backupPath), 0755); err != nil {
		return fmt.Errorf("mkdir notebook crypto backup dir failed: %w", err)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Ensure the KEK is derived (password change/enable completed) before saving the backup
  2. Retry the enabling/password-change operation that supplies the KEK
  3. Never bypass the guard by writing a MAC-less backup
Defensive patterns

Strategy: type-guard

When it happens

Trigger: Thrown at kernel/model/crypto.go:382 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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