siyuan-note/siyuan · error

cannot replay block swap across encrypted notebook…

Error message

cannot replay block swap across encrypted notebook boundaries

What it means

During block swap replay (undo/redo), the kernel re-collects the trees touched by the stored fragments and re-verifies that every tree's notebook is on the same crypto boundary as the first one. This error means the replay state now spans notebooks with incompatible encryption boundaries — typically because a block or tree was moved to a different notebook between the original swap and the replay.

Solutions

  1. Restore the involved blocks to their original notebooks, then retry undo/redo
  2. If the new location is intended, discard the undo history for that swap instead of replaying it
  3. Align notebook encryption settings across clients and re-sync
Defensive patterns

Strategy: try-catch

Validate before calling

const boxes = touchedTrees.map(t => t.box);
if (new Set(boxes).size > 1 && !sameCryptoBoundary(boxes)) throw new Error('replay would cross encrypted notebook boundary');

Try / catch

try { await replay(tx); } catch (e) {
  if (e.message.includes('cannot replay block swap across encrypted notebook boundaries')) { /* drop undo entry or restore notebooks first */ }
}

Prevention

When it happens

Trigger: Undoing/redoing a block swap after one of the involved trees was moved to a notebook with a different crypto boundary; replaying synced state where notebooks differ in encryption across clients.

Common situations: Moving a swapped block to another notebook, then pressing undo; mixing encrypted and plain notebooks in a synced workspace; restoring a partial snapshot.

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/3c74af731a26b3c8. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/transaction_block_swap.go:210

func (tx *Transaction) replayBlockSwap(operation *Operation) error {
	state := operation.blockSwapState
	from, to := state.before, state.after
	if operation.blockSwapUndo {
		from, to = to, from
	}
	trees := map[string]*parse.Tree{}
	var orderedTrees []*parse.Tree
	for _, rootID := range state.rootIDs {
		tree, err := tx.loadTree(rootID)
		if err != nil {
			return err
		}
		trees[rootID] = tree
		orderedTrees = append(orderedTrees, tree)
	}
	for _, tree := range orderedTrees {
		if !IsSameCryptoBoundary(orderedTrees[0].Box, tree.Box) {
			return errors.New("cannot replay block swap across encrypted notebook boundaries")
		}
	}
	tx.saveBlockSwapOriginalTrees(orderedTrees)
	if err := restoreBlockSwapFragments(from, to, trees); err != nil {
		return err
	}
	tx.finishBlockSwap(from, to, orderedTrees)
	operation.RetData = state.rootIDs
	return nil
}

func restoreBlockSwapFragments(from, to []blockSwapFragment, trees map[string]*parse.Tree) error {
	removing := map[string]bool{}
	for _, fragment := range from {
		tree := trees[fragment.rootID]
		if tree == nil || tree.Box != fragment.boxID {
			return errors.New("block swap notebook has changed")
		}

View on GitHub (pinned to 9f775e8a12)