{"record":{"id":"8d5033187c7de2b5","repo":"siyuan-note/siyuan","slug":"encrypted-database-history-is-plaintext","errorCode":null,"errorMessage":"encrypted database history is plaintext","messagePattern":"encrypted database history is plaintext","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view_block_history.go","lineNumber":29,"sourceCode":"\t\"github.com/88250/lute/ast\"\n\t\"github.com/88250/lute/parse\"\n\t\"github.com/siyuan-note/filelock\"\n\t\"github.com/siyuan-note/siyuan/kernel/av\"\n\t\"github.com/siyuan-note/siyuan/kernel/util\"\n)\n\nfunc boundAttributeViewHistoryPath(historyDir, boxID, avID string) string {\n\treturn filepath.Join(historyDir, boxID, \"storage\", \"av\", avID+\".json\")\n}\n\nfunc readBoundAttributeViewHistory(filename, boxID, avID string) (*av.AttributeView, error) {\n\tdata, err := filelock.ReadFile(filename)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif boxID != \"\" {\n\t\tif !util.IsCiphertext(data) {\n\t\t\treturn nil, errors.New(\"encrypted database history is plaintext\")\n\t\t}\n\t\tdata, err = av.DecryptAVData(boxID, avID, data)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else if util.IsCiphertext(data) {\n\t\treturn nil, errors.New(\"encrypted database history has no notebook context\")\n\t}\n\tview, err := av.ParseAttributeViewData(avID, data)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif view.ID != avID {\n\t\treturn nil, errors.New(\"database history ID does not match its filename\")\n\t}\n\tfor _, kv := range view.KeyValues {\n\t\tif kv == nil || kv.Key == nil {\n\t\t\treturn nil, errors.New(\"database history contains an invalid field\")","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view_block_history.go#L11-L47","documentation":"While reading a bound attribute view's history snapshot, the engine requires that history data belonging to a notebook (boxID non-empty) is ciphertext under the encrypted-notebook format. Finding plaintext bytes where the box enforces encryption means the history file was written by an older/non-encrypting writer or tampered with, so it is rejected instead of silently accepting unauthenticated data.","triggerScenarios":"restoreAttributeViewHistory, restoreEmbeddedAttributeViewHistory, or backupBoundAttributeViewHistory reading a history file for an encrypted notebook's database where util.IsCiphertext(data) returns false — e.g. a history file produced before encryption was enabled, copied from a plaintext workspace, or corrupted so the ciphertext header is lost.","commonSituations":"Restoring database history after migrating notebooks between encrypted and non-encrypted workspaces; mixing history directories across versions; manually editing or syncing history files.","solutions":["Do not import plaintext history into an encrypted notebook; regenerate history from the original encrypted workspace","Verify the history file's boxID matches a notebook that was actually encrypted at write time","Re-sync or re-create history by triggering a new snapshot of the database","Restore the original ciphertext file from backup if it was overwritten or corrupted"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const data = await readFile(filename);\nif (boxID && !isCiphertext(data)) {\n  throw new Error('history file is plaintext; cannot use with encrypted notebook');\n}","typeGuard":"function isUsableEncryptedHistory(data, boxID) {\n  return !boxID || isCiphertext(data);\n}","tryCatchPattern":null,"preventionTips":["Never copy history files from plaintext workspaces into encrypted notebooks","Keep history directories per-workspace; do not merge them across setups","Back up encrypted notebooks together with their history and key material","Upgrade notebooks through the supported migration instead of hand-copying files"],"tags":["database","history","encryption","ciphertext"],"backgroundTag":"encrypted-history-format-mismatch","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}