{"record":{"id":"30a676f66f17b690","repo":"siyuan-note/siyuan","slug":"unsupported-notebook-crypto-backup-spec-d","errorCode":null,"errorMessage":"unsupported notebook crypto backup spec [%d]","messagePattern":"unsupported notebook crypto backup spec \\[(.+?)\\]","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto.go","lineNumber":494,"sourceCode":"\t}\n\treturn true\n}\n\n// loadNotebookCryptoBackup 从 DataDir 读取 NotebookCrypto 备份。文件不存在返回 (nil, nil)。\nfunc loadNotebookCryptoBackup() (*conf.NotebookCrypto, error) {\n\tdata, err := filelock.ReadFile(dataCryptoBackupPath())\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tnc := &conf.NotebookCrypto{}\n\tif err := json.Unmarshal(data, nc); err != nil {\n\t\treturn nil, err\n\t}\n\tif nc.Spec != conf.CurrentNotebookCryptoSpec {\n\t\treturn nil, fmt.Errorf(\"unsupported notebook crypto backup spec [%d]\", nc.Spec)\n\t}\n\tif !notebookCryptoConfigurationComplete(nc) {\n\t\treturn nil, errors.New(\"notebook crypto backup is incomplete or corrupted\")\n\t}\n\treturn nc, nil\n}\n\n// removeNotebookCryptoBackup 删除备份文件（禁用加密功能时调用）。文件不存在视为成功。\nfunc removeNotebookCryptoBackup() {\n\tif err := os.Remove(dataCryptoBackupPath()); err != nil && !os.IsNotExist(err) {\n\t\tlogging.LogErrorf(\"remove notebook crypto backup failed: %s\", err)\n\t}\n}\n\n// masterPasswordMigration 记录改密迁移的完整状态，用于崩溃后恢复。\ntype masterPasswordMigration struct {\n\tOldVerifier      []byte              `json:\"oldVerifier\"`\n\tNewVerifier      []byte              `json:\"newVerifier\"`","sourceCodeStart":476,"sourceCodeEnd":512,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/crypto.go#L476-L512","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: backup written by older spec\n// {\"Spec\":1,...} but CurrentNotebookCryptoSpec == 2\n// after: regenerate backup on current version\nnc.KEKMAC = computeKEKMAC(nc, kek)\n_ = writeNotebookCryptoBackupData(nc, kek) // writes Spec: 2","handlingStrategy":"validation","validationCode":"var probe struct{ Spec int `json:\"Spec\"` }\nif err := json.Unmarshal(rawBackup, &probe); err != nil {\n    return fmt.Errorf(\"unreadable backup: %w\", err)\n}\nif probe.Spec != conf.CurrentNotebookCryptoSpec {\n    return fmt.Errorf(\"backup spec %d != current %d; upgrade or regenerate\", probe.Spec, conf.CurrentNotebookCryptoSpec)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["go","encryption","versioning","backup"],"backgroundTag":"unsupported-enum-value","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}