{"record":{"id":"d892ea0fecc1a2a7","repo":"siyuan-note/siyuan","slug":"encrypted-document-history-is-missing-notebook-con","errorCode":null,"errorMessage":"encrypted document history is missing notebook context","messagePattern":"encrypted document history is missing notebook context","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/history.go","lineNumber":183,"sourceCode":"\thistoryPath, err = validateHistoryPath(historyPath)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tdata, err := filelock.ReadFile(historyPath)\n\tif err != nil {\n\t\tlogging.LogErrorf(\"read file [%s] failed: %s\", historyPath, err)\n\t\treturn\n\t}\n\n\t// 加密笔记本的历史是密文，按路径里的 boxID 解密后解析\n\trelPath := strings.TrimPrefix(filepath.ToSlash(historyPath), filepath.ToSlash(util.HistoryDir))\n\trelPath = strings.TrimPrefix(relPath, \"/\")\n\tpathParts := strings.SplitN(relPath, \"/\", 3)\n\tciphertext := util.IsCiphertext(data)\n\tif ciphertext {\n\t\tif len(pathParts) < 3 || !ast.IsNodeIDPattern(pathParts[1]) {\n\t\t\terr = errors.New(\"encrypted document history is missing notebook context\")\n\t\t\treturn\n\t\t}\n\t\thistBoxID := pathParts[1]\n\t\tif !IsEncryptedBox(histBoxID) {\n\t\t\terr = fmt.Errorf(\"encrypted document history has no matching notebook [%s]\", histBoxID)\n\t\t\treturn\n\t\t}\n\t\tHoldBoxReadLock(histBoxID)\n\t\tdefer ReleaseBoxReadLock(histBoxID)\n\t\tdek, dekErr := GetDEKIfUnlocked(histBoxID)\n\t\tif dekErr != nil {\n\t\t\terr = errors.New(Conf.Language(314))\n\t\t\treturn\n\t\t}\n\t\tdata, err = DecryptFile(histBoxID, pathParts[2], dek, data)\n\t\tif err != nil {\n\t\t\tlogging.LogErrorf(\"decrypt history [%s] failed: %s\", historyPath, err)\n\t\t\treturn","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/history.go#L165-L201","documentation":"GetDocHistoryContent reads a history snapshot file. If the file bytes are ciphertext, the history path must contain a notebook ID segment (history/<timestamp>/<boxID>/<file>) so the kernel can find the right notebook. When the path has fewer than 3 parts or the second segment is not a node-ID-shaped notebook ID, it errors with 'encrypted document history is missing notebook context'.","triggerScenarios":"Requesting a history file whose path lacks the notebook-ID segment, or where pathParts[1] is not a valid node ID, while the file content is detected as ciphertext (util.IsCiphertext).","commonSituations":"Hand-built history paths in plugins/scripts; very old history layout created before encrypted-notebook support; corrupted or renamed history directories.","solutions":["Pass the full original historyPath returned by getDocHistoryContent/history listing endpoints, not a reconstructed path","Verify the path matches history/<ts>/<boxID>/<name> for encrypted notebooks","Restore or re-index the history directory if it was manually modified"],"exampleFix":"// before\nconst p = '/history/20240101120000/index.sy'; // missing boxID segment\n// after\nconst p = '/history/20240101120000/20240101100000-abc1234567890/index.sy';","handlingStrategy":"validation","validationCode":"// expected layout: history/<ts>/<boxID>/<name>\nconst parts = historyPath.replace(/^\\/history\\//, '').split('/');\nif (parts.length < 3 || !/^[0-9a-v]{20}$/.test(parts[1])) throw new Error('history path missing notebook context');","typeGuard":"const hasBoxContext = (p) => { const parts = p.replace(/^\\/history\\//, '').split('/'); return parts.length >= 3 && /^[0-9a-v]{20}$/.test(parts[1]); };","tryCatchPattern":"try {\n  return await fetchPost('/api/history/getDocHistoryContent', {historyPath});\n} catch (e) {\n  if (String(e.msg).includes('missing notebook context')) {\n    // re-fetch the canonical history path from the history listing API\n  } else throw e;\n}","preventionTips":["Always use history paths as returned by the history APIs; never assemble them by hand","Keep the full 3-segment path (ts/boxID/file) for encrypted notebooks","Do not rename or reorganize files under data/history/"],"tags":["history","encrypted-notebook","path"],"backgroundTag":"invalid-argument-format","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"}