siyuan-note/siyuan · error

unsupported notebook crypto backup spec [%d]

Error message

unsupported notebook crypto backup spec [%d]

What it means

loadNotebookCryptoBackup parsed the backup JSON but its Spec field does not match conf.CurrentNotebookCryptoSpec, so the library refuses to load it. Backups are versioned; only backups written by the current spec are trusted to avoid restoring incompatible or downgraded crypto material.

Source

Thrown at kernel/model/crypto.go:494

	}
	return true
}

// loadNotebookCryptoBackup 从 DataDir 读取 NotebookCrypto 备份。文件不存在返回 (nil, nil)。
func loadNotebookCryptoBackup() (*conf.NotebookCrypto, error) {
	data, err := filelock.ReadFile(dataCryptoBackupPath())
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, err
	}
	nc := &conf.NotebookCrypto{}
	if err := json.Unmarshal(data, nc); err != nil {
		return nil, err
	}
	if nc.Spec != conf.CurrentNotebookCryptoSpec {
		return nil, fmt.Errorf("unsupported notebook crypto backup spec [%d]", nc.Spec)
	}
	if !notebookCryptoConfigurationComplete(nc) {
		return nil, errors.New("notebook crypto backup is incomplete or corrupted")
	}
	return nc, nil
}

// removeNotebookCryptoBackup 删除备份文件(禁用加密功能时调用)。文件不存在视为成功。
func removeNotebookCryptoBackup() {
	if err := os.Remove(dataCryptoBackupPath()); err != nil && !os.IsNotExist(err) {
		logging.LogErrorf("remove notebook crypto backup failed: %s", err)
	}
}

// masterPasswordMigration 记录改密迁移的完整状态,用于崩溃后恢复。
type masterPasswordMigration struct {
	OldVerifier      []byte              `json:"oldVerifier"`
	NewVerifier      []byte              `json:"newVerifier"`

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Upgrade the kernel to the version that wrote the backup so CurrentNotebookCryptoSpec matches
  2. Re-create the backup from the current version's configuration instead of restoring the old one
  3. Do not hand-edit the backup file's Spec field; restore a complete, current-spec backup
  4. Check the log message for the actual spec number to identify which version produced it

Example fix

// before: backup written by older spec
// {"Spec":1,...} but CurrentNotebookCryptoSpec == 2
// after: regenerate backup on current version
nc.KEKMAC = computeKEKMAC(nc, kek)
_ = writeNotebookCryptoBackupData(nc, kek) // writes Spec: 2
Defensive patterns

Strategy: validation

Validate before calling

var probe struct{ Spec int `json:"Spec"` }
if err := json.Unmarshal(rawBackup, &probe); err != nil {
    return fmt.Errorf("unreadable backup: %w", err)
}
if probe.Spec != conf.CurrentNotebookCryptoSpec {
    return fmt.Errorf("backup spec %d != current %d; upgrade or regenerate", probe.Spec, conf.CurrentNotebookCryptoSpec)
}

Prevention

When it happens

Trigger: Calling restoreNotebookCryptoConfigFromBackup / deriveKEK while the backup file on disk contains a Spec value from an older or newer kernel version, or a hand-edited backup with a wrong Spec number.

Common situations: Downgrading SiYuan after the spec was bumped, restoring a data directory from an older installation, or manually editing the backup JSON and corrupting the spec field.

Related errors


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