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
- Upgrade the kernel to the version that wrote the backup so CurrentNotebookCryptoSpec matches
- Re-create the backup from the current version's configuration instead of restoring the old one
- Do not hand-edit the backup file's Spec field; restore a complete, current-spec backup
- 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
- Do not restore data directories from installations of very different versions without upgrading first
- Never hand-edit the Spec field of the backup JSON
- Pin the kernel version when migrating workspaces between machines
- Check the logged spec number to identify the producing version before retrying
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
- cannot write incomplete notebook crypto backup
- 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/30a676f66f17b690.
Report an issue: GitHub.