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

  1. Pass a historyPath that points to a document snapshot ending in .sy (e.g. history/<timestamp>/<boxID>/<docID>.sy).
  2. Use the history listing API to obtain valid document history paths instead of hand-building them.
  3. If the file was renamed or removed, pick the correct timestamp entry that still contains the .sy document.
  4. 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

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


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)