siyuan-note/siyuan · error · TxErr

block swap must be submitted as a separate transaction

Error message

block swap must be submitted as a separate transaction

What it means

This error means a block swap operation was submitted in a transaction together with other operations. The kernel requires that a 'swap-block-ref' operation be the sole operation in its transaction, because the swap manipulates two whole trees and stores an undo state snapshot that only makes sense atomically. doSwapBlockRef rejects any transaction whose DoOperations slice length is not exactly 1.

Solutions

  1. Submit the swap-block-ref operation alone in its own transaction with exactly one entry in the do array
  2. Move any other operations into separate transaction calls before or after the swap
  3. If batching is required, perform the swap last, in a dedicated transaction

Example fix

// before
POST /api/transactions
{"transactions":[{"doOperations":[{"action":"insert","data":"..."},{"action":"swap-block-ref",...}],"undoOperations":[]}]}
// after
POST /api/transactions
{"transactions":[{"doOperations":[{"action":"swap-block-ref",...}],"undoOperations":[]}]}
// then a second call for the insert
Defensive patterns

Strategy: validation

Validate before calling

const isSwapOnly = tx.doOperations.length === 1 && tx.doOperations[0].action === 'swap-block-ref';
if (!isSwapOnly) throw new Error('swap-block-ref must be submitted as the only operation in its transaction');

Prevention

When it happens

Trigger: Calling the transactions API with a 'swap-block-ref' operation bundled alongside other do/undo operations in the same transaction payload; performing a multi-operation batch that includes a block swap.

Common situations: Plugins or scripts batching several editor mutations into one transaction for efficiency; frontend code refactoring that merges a swap into an existing transaction array; replay/undo pipelines that concatenate queued operations.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/265e9529e3b94c58. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/transaction_block_swap.go:36

}

// 撤销快照仅保存在内核内存中,客户端不能提交或替换快照。
type blockSwapState struct {
	before, after []blockSwapFragment
	rootIDs       []string
}

type blockSwapFragment struct {
	rootID, boxID, previousID, nextID string
	node                              *ast.Node
}

func (tx *Transaction) doSwapBlockRef(operation *Operation) *TxErr {
	fail := func(err error) *TxErr {
		return &TxErr{code: TxErrCodePushMsg, id: operation.ID, msg: err.Error()}
	}
	if len(tx.DoOperations) != 1 {
		return fail(errors.New("block swap must be submitted as a separate transaction"))
	}
	if tx.isReplay {
		if operation.blockSwapState == nil {
			return fail(errors.New("block swap undo state is unavailable"))
		}
		if err := tx.replayBlockSwap(operation); err != nil {
			return fail(err)
		}
		return nil
	}
	data, err := json.Marshal(operation.Data)
	if err != nil {
		return fail(err)
	}
	var options blockSwapOptions
	if err = json.Unmarshal(data, &options); err != nil || options.IncludeChildren == nil || options.OriginalToEmbed == nil {
		return fail(errors.New("invalid block swap options"))
	}

View on GitHub (pinned to 9f775e8a12)