siyuan-note/siyuan · error
encrypted attribute view history is missing valid notebook c
Error message
encrypted attribute view history is missing valid notebook context
What it means
RollbackAttributeViewHistory validates that an encrypted AV history snapshot can be attributed to an encrypted notebook. The history file content is ciphertext (util.IsCiphertext), so the code requires the path under history dir to be boxID/avDir/file.json where pathParts[1] is a valid node ID matching an encrypted box. If the history path does not carry a valid encrypted notebook ID at that position, the rollback cannot determine where the AV definition belongs and it aborts. This protects against restoring encrypted AV definitions to the global storage/av directory where they would be inaccessible or wrongly placed.
Source
Thrown at kernel/model/history.go:692
return
}
// 验证目标文件必须是 AV 定义文件
if !strings.HasSuffix(historyPath, ".json") || !strings.Contains(filepath.ToSlash(historyPath), "/storage/av/") {
return fmt.Errorf("invalid AV history path [%s]", historyPath)
}
from := historyPath
// 从路径提取 boxID 判断是否加密笔记本的 AV
relPath := strings.TrimPrefix(filepath.ToSlash(historyPath), filepath.ToSlash(util.HistoryDir))
relPath = strings.TrimPrefix(relPath, "/")
pathParts := strings.SplitN(relPath, "/", 3)
data, readErr := filelock.ReadFile(from)
if readErr != nil {
return readErr
}
ciphertext := util.IsCiphertext(data)
if ciphertext && (len(pathParts) < 3 || !ast.IsNodeIDPattern(pathParts[1]) || !IsEncryptedBox(pathParts[1])) {
return errors.New("encrypted attribute view history is missing valid notebook context")
}
to := filepath.Join(util.DataDir, "storage", "av", filepath.Base(historyPath))
if len(pathParts) >= 2 && IsEncryptedBox(pathParts[1]) {
if !ciphertext {
return fmt.Errorf("encrypted notebook attribute view history is plaintext [%s]", pathParts[1])
}
// 加密笔记本的 AV 定义回滚到笔记本级目录
to = filepath.Join(util.DataDir, pathParts[1], "storage", "av", filepath.Base(historyPath))
if err = os.MkdirAll(filepath.Dir(to), 0755); err != nil {
return
}
}
if err = filelock.CopyNewtimes(from, to); err != nil {
logging.LogErrorf("copy file [%s] to [%s] failed: %s", from, to, err)
return
}
avID := strings.TrimSuffix(filepath.Base(historyPath), ".json")View on GitHub (pinned to 8641553a1f)
Solutions
- Ensure the history path follows <historyDir>/<timestamp>-<op>/<boxID>/storage/av/<avID>.json where boxID is a valid node ID of an encrypted notebook
- Verify the boxID segment actually belongs to an encrypted notebook (IsEncryptedBox) — regenerate or re-locate the history under the correct notebook ID directory
- If the history file was manually moved, restore the original directory structure from backup
- If the AV content is not actually encrypted, check why util.IsCiphertext returned true (wrong file passed in)
Example fix
// before
RollbackAttributeViewHistory("/history/20240101120000-update/storage/av/20240101100000-abc.json") // missing boxID segment
// after
RollbackAttributeViewHistory("/history/20240101120000-update/20240101100000-boxid/storage/av/20240101100000-abc.json") Defensive patterns
Strategy: validation
Validate before calling
const rel = historyPath.replace(/\\/g, "/").split("/history/").pop().replace(/^\//, "");
const parts = rel.split("/");
const isNodeID = (s) => /^[0-9]{14}-[0-9a-z]{7}$/.test(s);
if (parts.length < 3 || !isNodeID(parts[1])) {
throw new Error("history path lacks notebook context: " + historyPath);
} Prevention
- Never move or rename directories inside the history tree by hand
- Always take history paths from the history query API, not from manual filesystem scans
- Keep backups of the full history directory structure, not flattened copies
When it happens
Trigger: Calling RollbackAttributeViewHistory with a historyPath whose content decrypts as ciphertext but whose relative path under the history directory has fewer than 3 segments, or whose second segment (boxID) is not a node-ID pattern or does not correspond to an encrypted notebook, e.g. a hand-moved or legacy AV history file stored without a notebook-level directory.
Common situations: Manually copying AV history files between machines or backup restores that flatten the history directory structure; upgrading from a version where encrypted-notebook AV history was stored in a different layout; pointing the history viewer at a stale/corrupted history tree.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- encrypted attribute view history is missing notebook context
- encrypted asset history is missing valid notebook context
- invalid AV history path [%s]
- encrypted notebook attribute view history is plaintext [%s]
- CLI does not support encrypted notebook history
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/1faee020d3ad5137.
Report an issue: GitHub.