{"record":{"id":"78369f838790e26f","repo":"siyuan-note/siyuan","slug":"parse-document-history-failed-w","errorCode":null,"errorMessage":"parse document history failed: %w","messagePattern":"parse document history failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/history.go","lineNumber":364,"sourceCode":"\t\tdek, dekErr := GetDEKIfUnlocked(origBoxID)\n\t\tif dekErr != nil {\n\t\t\terr = errors.New(Conf.Language(314))\n\t\t\treturn\n\t\t}\n\t\tvar decErr error\n\t\t// 历史路径格式：<historyDir>/<datePrefix>/<boxID>/<relativePath>\n\t\t// 用原始 boxID 解密（getRollbackBox 可能创建了新 box，密文仍属于原加密 box）\n\t\tfilePath := parts[2]\n\t\tsrcData, decErr = DecryptFile(origBoxID, filePath, dek, srcData)\n\t\tif decErr != nil {\n\t\t\tlogging.LogErrorf(\"decrypt history [%s] failed: %s\", srcPath, decErr)\n\t\t\terr = decErr\n\t\t\treturn\n\t\t}\n\t}\n\ttree, parseErr := loadTreeByData0(srcData)\n\tif parseErr != nil {\n\t\treturn fmt.Errorf(\"parse document history failed: %w\", parseErr)\n\t}\n\tif tree == nil {\n\t\treturn errors.New(\"parse document history failed\")\n\t}\n\tif encrypted && tree.Root.ID+\".sy\" != filepath.Base(historyPath) {\n\t\treturn errors.New(\"encrypted document history root ID does not match its filename\")\n\t}\n\tif nil != tree {\n\t\thistoryDir := filepath.Join(util.HistoryDir, parts[0])\n\n\t\tavNodes := tree.Root.ChildrenByType(ast.NodeAttributeView)\n\t\tfor _, avNode := range avNodes {\n\t\t\tsrcAvPath := filepath.Join(historyDir, \"storage\", \"av\", avNode.AttributeViewID+\".json\")\n\t\t\t// 加密笔记本的 AV 定义在笔记本级目录\n\t\t\tdestAvPath := filepath.Join(util.DataDir, \"storage\", \"av\", avNode.AttributeViewID+\".json\")\n\t\t\tif IsEncryptedBox(boxID) {\n\t\t\t\t// 历史目录里 AV 也可能在 boxID 子目录下\n\t\t\t\tboxSrcAvPath := filepath.Join(historyDir, boxID, \"storage\", \"av\", avNode.AttributeViewID+\".json\")","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/history.go#L346-L382","documentation":"After any required decryption, RollbackDocHistory parses the snapshot bytes into a document tree with loadTreeByData0. If parsing fails (malformed .sy JSON/AST, truncated file, wrong data after a failed decrypt), the error is wrapped with this prefix so the caller knows the history snapshot itself could not be loaded, distinguishing it from I/O or key errors.","triggerScenarios":"rollbackDocHistory reaches loadTreeByData0 and it returns an error: the history .sy file is truncated/corrupted, contains invalid JSON, or decryption produced garbage bytes (e.g. wrong key handled without detection).","commonSituations":"Disk corruption or interrupted write left a partial history file; manual editing of history .sy files broke the JSON; a decryption step returned data that is not a valid tree; version incompatibility where an old snapshot uses an unsupported format.","solutions":["Inspect the history file at the reported path — check it is complete valid JSON with a root node","Choose a different (older/newer) history snapshot for the same document","Restore the history file from backup if it was truncated; do not hand-patch the AST","Ensure decryption is using the correct DEK; wrong-key output can fail parsing"],"exampleFix":"// before: rolling back to a corrupted snapshot\nfetchPost(\"/api/history/rollbackDocHistory\", {historyPath: \"data/history/20260910/box/doc.sy\"})\n// after: verify the snapshot parses before rolling back\nconst raw = await fetchPost(\"/api/file/getFile\", {path: historyPath})\nJSON.parse(raw) // throws here instead of inside rollback","handlingStrategy":"try-catch","validationCode":"const raw = await fetchPost(\"/api/file/getFile\", {path: historyPath})\nJSON.parse(raw) // validate the snapshot parses before rollback","typeGuard":null,"tryCatchPattern":"try {\n  await fetchPost(\"/api/history/rollbackDocHistory\", {historyPath})\n} catch (e) {\n  if (String(e.msg || e).includes(\"parse document history failed\")) {\n    // pick another snapshot for the same document\n  }\n}","preventionTips":["Avoid interrupting the app during history writes","Never hand-edit history .sy files","Keep workspace disk healthy; corrupted sectors commonly corrupt history snapshots"],"tags":["go","history","parse","corruption"],"backgroundTag":"json-parse-error","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"}