siyuan-note/siyuan · error

invalid created doc compensation snapshot

Error message

invalid created doc compensation snapshot

What it means

restoreCreatedDocTreeSnapshot compensates a failed transaction by restoring the tree of a document that was created within it. It throws this error when it is handed a nil tree or a tree with a nil Root, which means the compensation snapshot passed to the transaction rollback path is unusable and the created document's tree cannot be re-persisted.

Source

Thrown at kernel/model/transaction.go:2871

		}
		if restoreErr := restoreCreatedDocTreeSnapshot(tree); nil != restoreErr {
			err = errors.Join(err, restoreErr)
			continue
		}
		if box := Conf.Box(tree.Box); nil != box {
			box.setSortByConf(path.Dir(tree.Path), tree.ID)
			PushCreate(box, tree.Path, nil)
		}
	}
	if nil != tx.templateDocTreeRootSnapshot {
		err = errors.Join(err, restoreCreatedDocTreeSnapshot(tx.templateDocTreeRootSnapshot))
	}
	return
}

func restoreCreatedDocTreeSnapshot(tree *parse.Tree) error {
	if nil == tree || nil == tree.Root {
		return errors.New("invalid created doc compensation snapshot")
	}
	if err := writeTreeUpsertQueue(tree); nil != err {
		return err
	}
	treenode.UpsertBlockTree(tree)
	return nil
}

func (tx *Transaction) loadTreeByBlockTree(bt *treenode.BlockTree) (ret *parse.Tree, err error) {
	if nil == bt {
		return nil, ErrBlockNotFound
	}

	ret = tx.trees[bt.RootID]
	if nil != ret {
		return
	}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Ensure the created document's tree is loaded/parsed successfully before initiating the transaction compensation, and skip or log when no tree exists
  2. Check that the .sy file for the created doc exists and is valid JSON Kramdown in the notebook's data directory
  3. Re-open the notebook / rebuild the index so the tree can be re-fetched, then retry the transaction
  4. Inspect the caller of restoreCreatedDocTreeSnapshot to make sure it does not forward a nil tree from a failed load without handling it

Example fix

// before
restoreCreatedDocTreeSnapshot(tree)
// after
if tree != nil && tree.Root != nil {
    if err := restoreCreatedDocTreeSnapshot(tree); err != nil {
        logging.LogErrorf("restore created doc snapshot failed: %v", err)
    }
}
Defensive patterns

Strategy: type-guard

Validate before calling

if tree == nil || tree.Root == nil {
    return errors.New("cannot compensate: snapshot tree is empty")
}
restoreCreatedDocTreeSnapshot(tree)

Type guard

func hasRoot(tree *parse.Tree) bool { return tree != nil && tree.Root != nil }

Prevention

When it happens

Trigger: A transaction includes a document creation, later fails, and the compensation path calls restoreCreatedDocTreeSnapshot with a nil tree or a parse.Tree whose Root is nil (e.g. the tree failed to load/parse before the compensation ran).

Common situations: Rollback after a partial multi-doc transaction where the created doc's tree was never indexed; corrupted or not-yet-written .sy file loaded into a tree with no root; a nil result from a tree loader passed straight into the compensation function.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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