{"record":{"id":"57421fc21b3d3113","repo":"siyuan-note/siyuan","slug":"history-path-s-not-exist","errorCode":null,"errorMessage":"history path [%s] not exist","messagePattern":"history path \\[(.+?)\\] not exist","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/history.go","lineNumber":593,"sourceCode":"\n\tif err = filelock.CopyNewtimes(from, to); err != nil {\n\t\tlogging.LogErrorf(\"copy file [%s] to [%s] failed: %s\", from, to, err)\n\t\treturn\n\t}\n\tIncSync()\n\tutil.PushMsg(Conf.Language(102), 3000)\n\treturn nil\n}\n\n// validateHistoryPath 校验历史路径是否位于工作区内且属于历史目录。\n// 拒绝路径穿越攻击（..、绝对路径等）。返回规范化的绝对路径。\nfunc validateHistoryPath(historyPath string) (string, error) {\n\tp := filepath.Join(util.WorkspaceDir, historyPath)\n\tif !gulu.File.IsSubPath(util.WorkspaceDir, p) {\n\t\treturn \"\", fmt.Errorf(\"history path [%s] is not in workspace\", historyPath)\n\t}\n\tif !gulu.File.IsExist(p) {\n\t\treturn \"\", fmt.Errorf(\"history path [%s] not exist\", historyPath)\n\t}\n\trel, err := filepath.Rel(util.HistoryDir, p)\n\tif err != nil || strings.HasPrefix(rel, \"..\") {\n\t\treturn \"\", fmt.Errorf(\"history path [%s] is not under history directory\", historyPath)\n\t}\n\treturn p, nil\n}\n\n// IsEncryptedHistoryPath 判断历史路径是否明确属于加密笔记本。\nfunc IsEncryptedHistoryPath(absPath string) bool {\n\tboxID := ExtractBoxIDFromHistoryPath(absPath)\n\tif boxID == \"\" {\n\t\treturn false\n\t}\n\tif IsEncryptedBox(boxID) {\n\t\treturn true\n\t}\n\trel, err := filepath.Rel(util.HistoryDir, absPath)","sourceCodeStart":575,"sourceCodeEnd":611,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/history.go#L575-L611","documentation":"validateHistoryPath checks that the resolved history path actually exists on disk after confirming it is inside the workspace. If gulu.File.IsExist reports the file is absent, the caller asked to operate on a history entry that no longer exists.","triggerScenarios":"Calling any history API (GetDocHistoryContent, RollbackDocHistory, RollbackAssetsHistory, RollbackNotebookHistory, RollbackAttributeViewHistory, ResolveDocVersionBoxID) with a path whose file was already removed — typically by history auto-cleanup (retention limits), manual deletion of data/history, or a typo in the path.","commonSituations":"UI kept an open history list after the retention job pruned old entries; another client (sync/second device) deleted history meanwhile; stale book/bookmark referencing pruned snapshots; hard-coded paths in tests or scripts.","solutions":["Re-query the history list (/api/history/getHistoryItems) and use a currently existing path","Check retention settings (Settings - History) — the entry may have been auto-pruned; raise the retention period if needed","Verify the file exists in data/history before calling the API","Refresh the history panel so the UI list matches disk state"],"exampleFix":"// before: using a cached path that was pruned\nrollbackDocHistory(cachedHistoryPath)\n// after: re-list and pick a live entry\nconst items = await fetchPost(\"/api/history/getHistoryItems\", {query: \"\"});\nrollbackDocHistory(items[0].path)","handlingStrategy":"validation","validationCode":"async function historyPathExists(historyPath) {\n  const r = await fetchPost(\"/api/filetree/checkFileExist\", {path: historyPath});\n  return r.code === 0 && r.data.isExist === true;\n}","typeGuard":"null","tryCatchPattern":"try { await rollbackDocHistory(p); } catch (e) { if (String(e.msg).includes(\"not exist\")) { await refreshHistoryList(); /* pick a live entry */ } else { throw e; } }","preventionTips":["Refresh the history list before each rollback instead of caching paths","Be aware the history retention job prunes old snapshots automatically","Re-query history after sync operations from other devices"],"tags":["history","file-not-found","stale-state"],"backgroundTag":"file-not-found","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"}