siyuan-note/siyuan · error
history version is not a document
Error message
history version is not a document
What it means
Returned by loadHistoryDocVersion (history_diff.go:338) when the validated history path does not end in ".sy" (case-insensitive). SiYuan documents on disk are .sy files; only those are valid document-version sources. Non-.sy entries under the history tree (assets, conf.json, boxDocMeta) are intentionally rejected here.
Source
Thrown at kernel/model/history_diff.go:338
ret, err = filelock.ReadFile(absPath)
if err != nil || !encrypted {
return
}
dek, err := GetDEKIfUnlocked(blockTree.BoxID)
if err != nil {
return nil, errors.New(Conf.Language(314))
}
ret, err = DecryptFile(blockTree.BoxID, relPath, dek, ret)
return
}
func loadHistoryDocVersion(historyPath string) (ret *loadedDocVersion, err error) {
absPath, err := validateHistoryPath(historyPath)
if err != nil {
return nil, err
}
if !strings.HasSuffix(strings.ToLower(absPath), ".sy") {
return nil, errors.New("history version is not a document")
}
relPath, err := filepath.Rel(util.HistoryDir, absPath)
if err != nil {
return nil, err
}
parts := strings.SplitN(filepath.ToSlash(relPath), "/", 3)
data, err := filelock.ReadFile(absPath)
if err != nil {
return nil, err
}
ciphertext := util.IsCiphertext(data)
if ciphertext {
if len(parts) < 3 || !ast.IsNodeIDPattern(parts[1]) || !IsEncryptedBox(parts[1]) {
return nil, errors.New("encrypted document history is missing valid notebook context")
}
HoldBoxReadLock(parts[1])
defer ReleaseBoxReadLock(parts[1])
dek, dekErr := GetDEKIfUnlocked(parts[1])View on GitHub (pinned to 251596fc0d)
Solutions
- Filter history entries client-side to documents only (the history API already tags entry types).
- Before calling loadHistoryDocVersion, verify strings.HasSuffix(strings.ToLower(path), ".sy").
- If the user genuinely wants to diff an asset, use the asset-history endpoint, not the document-diff one.
Example fix
// before
ref := &DocVersionRef{Type: "history", Path: pickedPath}
// after
if !strings.HasSuffix(strings.ToLower(pickedPath), ".sy") {
return errors.New("history version is not a document")
}
ref := &DocVersionRef{Type: "history", Path: pickedPath} Defensive patterns
Strategy: validation
Validate before calling
if !strings.HasSuffix(strings.ToLower(historyPath), ".sy") {
return errors.New("history version is not a document")
}
// safe to load history doc version Type guard
// isHistoryDoc reports whether a history path is a .sy document.
func isHistoryDoc(p string) bool {
return strings.HasSuffix(strings.ToLower(p), ".sy")
} Prevention
- Filter the history list client-side to document entries only.
- Validate the .sy suffix before constructing a history-type DocVersionRef.
- Use asset-history endpoints for assets, not the document-diff endpoint.
When it happens
Trigger: POST /api/history/diffDocVersions with a history "path" pointing at a directory, an asset file, or a .json metadata file; a frontend that lets the user pick a non-document history row; a hand-crafted historyPath argument.
Common situations: User selects an asset or notebook-config row in the history panel; a script that walks the history dir and feeds every file to the diff API.
Related errors
- document version is required
- current document ID is invalid
- document versions do not belong to the same document
- document version is empty
- snapshot file ID is required
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/5e7a5aea6d21836b.
Report an issue: GitHub.