{"record":{"id":"a03636d5a6bddd15","repo":"siyuan-note/siyuan","slug":"invalid-av-history-path-s","errorCode":null,"errorMessage":"invalid AV history path [%s]","messagePattern":"invalid AV history path \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/history.go","lineNumber":678,"sourceCode":"\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"invalid notebook history path [%s]\", historyPath)\n\t}\n\tparts := strings.Split(filepath.ToSlash(rel), \"/\")\n\tif len(parts) != 2 || !strings.HasSuffix(parts[0], \"-\"+HistoryOpDelete) || !ast.IsNodeIDPattern(parts[1]) ||\n\t\t!gulu.File.IsDir(historyPath) || !filelock.IsExist(filepath.Join(historyPath, \".siyuan\", \"conf.json\")) {\n\t\treturn \"\", fmt.Errorf(\"invalid notebook history path [%s]\", historyPath)\n\t}\n\treturn parts[1], nil\n}\n\nfunc RollbackAttributeViewHistory(historyPath string) (err error) {\n\thistoryPath, err = validateHistoryPath(historyPath)\n\tif err != nil {\n\t\treturn\n\t}\n\t// 验证目标文件必须是 AV 定义文件\n\tif !strings.HasSuffix(historyPath, \".json\") || !strings.Contains(filepath.ToSlash(historyPath), \"/storage/av/\") {\n\t\treturn fmt.Errorf(\"invalid AV history path [%s]\", historyPath)\n\t}\n\n\tfrom := historyPath\n\t// 从路径提取 boxID 判断是否加密笔记本的 AV\n\trelPath := strings.TrimPrefix(filepath.ToSlash(historyPath), filepath.ToSlash(util.HistoryDir))\n\trelPath = strings.TrimPrefix(relPath, \"/\")\n\tpathParts := strings.SplitN(relPath, \"/\", 3)\n\tdata, readErr := filelock.ReadFile(from)\n\tif readErr != nil {\n\t\treturn readErr\n\t}\n\tciphertext := util.IsCiphertext(data)\n\tif ciphertext && (len(pathParts) < 3 || !ast.IsNodeIDPattern(pathParts[1]) || !IsEncryptedBox(pathParts[1])) {\n\t\treturn errors.New(\"encrypted attribute view history is missing valid notebook context\")\n\t}\n\tto := filepath.Join(util.DataDir, \"storage\", \"av\", filepath.Base(historyPath))\n\tif len(pathParts) >= 2 && IsEncryptedBox(pathParts[1]) {\n\t\tif !ciphertext {","sourceCodeStart":660,"sourceCodeEnd":696,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/history.go#L660-L696","documentation":"RollbackAttributeViewHistory first runs the generic history-path validation, then requires the target to look like an attribute-view (database) definition file: it must end in .json and sit inside a /storage/av/ segment of the history path. Anything else — doc .sy snapshots, asset files, or JSON files outside storage/av — is rejected with this error.","triggerScenarios":"Calling rollbackAttributeViewHistory with a document history path (.sy), an asset history file, or a JSON history file not located under <...>/storage/av/ (e.g. a .siyuan conf.json or widget config).","commonSituations":"Mixing up rollback endpoints for doc vs database history; databases stored in non-default layouts from third-party tooling; passing a database's live file instead of its history snapshot; plugin automation mapping the wrong history item type to this API.","solutions":["Pass only history paths containing /storage/av/<avID>.json, as returned by the database-history listing","Use rollbackDocHistory for .sy files and rollbackAssetsHistory for asset files instead","If the AV history file was moved, restore it to the storage/av/ layout of the snapshot before rolling back"],"exampleFix":"// before: doc snapshot passed to AV rollback\nrollbackAttributeViewHistory(\"history/20240101120000-update/<boxID>/<docID>.sy\")\n// after: AV definition snapshot\nrollbackAttributeViewHistory(\"history/20240101120000-update/<boxID>/storage/av/20240101120000-avxxxx.json\")","handlingStrategy":"validation","validationCode":"function isAvHistoryPath(historyPath) {\n  const normalized = historyPath.replace(/\\\\/g, \"/\");\n  return normalized.endsWith(\".json\") && normalized.includes(\"/storage/av/\");\n}","typeGuard":"null","tryCatchPattern":"try { await rollbackAttributeViewHistory(p); } catch (e) { if (String(e.msg).startsWith(\"invalid AV history path\")) { /* route .sy to rollbackDocHistory, assets to rollbackAssetsHistory */ } else { throw e; } }","preventionTips":["Match history item type to the correct rollback endpoint","Only pass JSON files under storage/av/ to the AV rollback API","Do not substitute live database files for their history snapshots"],"tags":["history","attribute-view","path-validation"],"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"}