siyuan-note/siyuan · error

parse document history failed: %w

Error message

parse document history failed: %w

What it means

After any required decryption, RollbackDocHistory parses the snapshot bytes into a document tree with loadTreeByData0. If parsing fails (malformed .sy JSON/AST, truncated file, wrong data after a failed decrypt), the error is wrapped with this prefix so the caller knows the history snapshot itself could not be loaded, distinguishing it from I/O or key errors.

Source

Thrown at kernel/model/history.go:364

		dek, dekErr := GetDEKIfUnlocked(origBoxID)
		if dekErr != nil {
			err = errors.New(Conf.Language(314))
			return
		}
		var decErr error
		// 历史路径格式:<historyDir>/<datePrefix>/<boxID>/<relativePath>
		// 用原始 boxID 解密(getRollbackBox 可能创建了新 box,密文仍属于原加密 box)
		filePath := parts[2]
		srcData, decErr = DecryptFile(origBoxID, filePath, dek, srcData)
		if decErr != nil {
			logging.LogErrorf("decrypt history [%s] failed: %s", srcPath, decErr)
			err = decErr
			return
		}
	}
	tree, parseErr := loadTreeByData0(srcData)
	if parseErr != nil {
		return fmt.Errorf("parse document history failed: %w", parseErr)
	}
	if tree == nil {
		return errors.New("parse document history failed")
	}
	if encrypted && tree.Root.ID+".sy" != filepath.Base(historyPath) {
		return errors.New("encrypted document history root ID does not match its filename")
	}
	if nil != tree {
		historyDir := filepath.Join(util.HistoryDir, parts[0])

		avNodes := tree.Root.ChildrenByType(ast.NodeAttributeView)
		for _, avNode := range avNodes {
			srcAvPath := filepath.Join(historyDir, "storage", "av", avNode.AttributeViewID+".json")
			// 加密笔记本的 AV 定义在笔记本级目录
			destAvPath := filepath.Join(util.DataDir, "storage", "av", avNode.AttributeViewID+".json")
			if IsEncryptedBox(boxID) {
				// 历史目录里 AV 也可能在 boxID 子目录下
				boxSrcAvPath := filepath.Join(historyDir, boxID, "storage", "av", avNode.AttributeViewID+".json")

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Inspect the history file at the reported path — check it is complete valid JSON with a root node
  2. Choose a different (older/newer) history snapshot for the same document
  3. Restore the history file from backup if it was truncated; do not hand-patch the AST
  4. Ensure decryption is using the correct DEK; wrong-key output can fail parsing

Example fix

// before: rolling back to a corrupted snapshot
fetchPost("/api/history/rollbackDocHistory", {historyPath: "data/history/20260910/box/doc.sy"})
// after: verify the snapshot parses before rolling back
const raw = await fetchPost("/api/file/getFile", {path: historyPath})
JSON.parse(raw) // throws here instead of inside rollback
Defensive patterns

Strategy: try-catch

Validate before calling

const raw = await fetchPost("/api/file/getFile", {path: historyPath})
JSON.parse(raw) // validate the snapshot parses before rollback

Try / catch

try {
  await fetchPost("/api/history/rollbackDocHistory", {historyPath})
} catch (e) {
  if (String(e.msg || e).includes("parse document history failed")) {
    // pick another snapshot for the same document
  }
}

Prevention

When it happens

Trigger: rollbackDocHistory reaches loadTreeByData0 and it returns an error: the history .sy file is truncated/corrupted, contains invalid JSON, or decryption produced garbage bytes (e.g. wrong key handled without detection).

Common situations: Disk corruption or interrupted write left a partial history file; manual editing of history .sy files broke the JSON; a decryption step returned data that is not a valid tree; version incompatibility where an old snapshot uses an unsupported format.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/78369f838790e26f. Report an issue: GitHub.