siyuan-note/siyuan · error
history version is not a document
Error message
history version is not a document
What it means
loadHistoryDocVersion validates that the requested history path points to a .sy document file before parsing it into a document tree. If the validated path does not end with .sy (case-insensitive), the loader refuses because history diff only works on document snapshots, not assets or other history artifacts.
Solutions
- Pass a historyPath that points to a document snapshot ending in .sy (e.g. history/<timestamp>/<boxID>/<docID>.sy).
- Use the history listing API to obtain valid document history paths instead of hand-building them.
- If the file was renamed or removed, pick the correct timestamp entry that still contains the .sy document.
- For asset history, use the appropriate asset-history API, not the document version loader.
Example fix
// before
loadDocVersion("history/20240101120000/20240101120000-abc/assets/foo.png")
// after
loadDocVersion("history/20240101120000/20240101120000-abc/20240101150000-def.sy") Defensive patterns
Strategy: validation
Validate before calling
// Go: validate the history path points to a .sy document before calling
if !strings.EqualFold(filepath.Ext(historyPath), ".sy") {
return errors.New("refusing to diff non-document history entry")
} Try / catch
catch (e) { if (e.message === "history version is not a document") { pickDocumentHistoryEntry(); } else { throw e; } } Prevention
- Always obtain historyPath from the history listing API, never hand-build it
- Filter history entries to .sy files before requesting version diffs
- Do not rename or move files inside the history directory
When it happens
Trigger: Calling the load-history-doc-version path (via loadDocVersion) with a historyPath that points to a non-document file under history/, e.g. an asset, .json index, or directory entry whose name lacks a .sy suffix.
Common situations: A caller passes a history record for an asset file instead of a document; stale UI links to a renamed/removed .sy; manually constructed historyPath from a history listing that includes non-document entries.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- attribute view history context is ambiguous
- attribute view snapshot context is ambiguous
- block [ ] is not a document
- block [ ] is not a document that can declare a child…
- can not get or create rollback box
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/5e7a5aea6d21836b.
Report an issue: GitHub.
Appendix: 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 9f775e8a12)