{"record":{"id":"137463c728e3ae65","repo":"siyuan-note/siyuan","slug":"encrypted-notebook-document-history-is-plaintext-137463","errorCode":null,"errorMessage":"encrypted notebook document history is plaintext [%s]","messagePattern":"encrypted notebook document history is plaintext \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/history_diff.go","lineNumber":365,"sourceCode":"\t\treturn nil, err\n\t}\n\tciphertext := util.IsCiphertext(data)\n\tif ciphertext {\n\t\tif len(parts) < 3 || !ast.IsNodeIDPattern(parts[1]) || !IsEncryptedBox(parts[1]) {\n\t\t\treturn nil, errors.New(\"encrypted document history is missing valid notebook context\")\n\t\t}\n\t\tHoldBoxReadLock(parts[1])\n\t\tdefer ReleaseBoxReadLock(parts[1])\n\t\tdek, dekErr := GetDEKIfUnlocked(parts[1])\n\t\tif dekErr != nil {\n\t\t\treturn nil, errors.New(Conf.Language(314))\n\t\t}\n\t\tdata, err = DecryptFile(parts[1], parts[2], dek, data)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else if len(parts) >= 2 && IsEncryptedBox(parts[1]) {\n\t\treturn nil, fmt.Errorf(\"encrypted notebook document history is plaintext [%s]\", parts[1])\n\t}\n\n\trootID := strings.TrimSuffix(filepath.Base(absPath), filepath.Ext(absPath))\n\ttree, err := parseDocVersionTree(data, rootID)\n\thistoryRoot := filepath.Join(util.HistoryDir, parts[0])\n\tboxID := \"\"\n\tif len(parts) >= 2 && ast.IsNodeIDPattern(parts[1]) {\n\t\tboxID = parts[1]\n\t}\n\tif err != nil {\n\t\treturn &loadedDocVersion{\n\t\t\ttitle:    rootID,\n\t\t\trootID:   rootID,\n\t\t\traw:      data,\n\t\t\tparseErr: err,\n\t\t\tlarge:    1024*1024 <= len(data),\n\t\t\tboxID:    boxID,\n\t\t\thistory:  historyRoot,","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/history_diff.go#L347-L383","documentation":"The history file belongs to an encrypted notebook (IsEncryptedBox) but its bytes are plaintext, not ciphertext. The kernel requires document history in encrypted notebooks to be stored encrypted; plaintext content there violates the format invariant, so it is rejected instead of silently reading unencrypted data.","triggerScenarios":"Loading history for an encrypted notebook where the .sy file under history/ is stored unencrypted — e.g. the file was written by a non-encrypting code path or manually copied from a normal notebook.","commonSituations":"Copying a plain notebook's history into an encrypted notebook's directory; a history-writing bug storing unencrypted data; toggling a notebook to encrypted after history existed in plaintext.","solutions":["Re-generate the history entry so the document is stored encrypted for that notebook","Move the history to a non-encrypted notebook if plaintext storage was intended","Remove the stale plaintext history entry and re-snapshot the document"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (isEncryptedBox(boxID) && !util.isCiphertext(data)) throw new Error(\"expected ciphertext for encrypted notebook\");","typeGuard":null,"tryCatchPattern":"try { await api.loadDocVersion(path); } catch (e) { if (String(e).includes(\"is plaintext\")) { showToast(\"History entry is corrupt; re-snapshot the document\"); } }","preventionTips":["Never copy plaintext .sy files into an encrypted notebook's history directory","Only use kernel APIs to write history for encrypted notebooks","Audit history directories after toggling a notebook between plain and encrypted"],"tags":["encryption","history","plaintext","notebook"],"backgroundTag":"incompatible-source-type","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"}