siyuan-note/siyuan · error
cannot swap a block with itself or its ancestor
Error message
cannot swap a block with itself or its ancestor
What it means
The two blocks to swap have an ancestor/descendant relationship (or are the same block). Swapping a block with itself or with something that contains it would create a cycle in the tree, so validateBlockSwap refuses it via the contains() helper that walks each node's ancestors.
Solutions
- Pick two sibling-level blocks that do not contain each other
- If a parent-child swap is intended, restructure as a move operation instead of a swap
- Check that the ref ID and def ID differ and neither is an ancestor of the other before submitting
Example fix
// before
// def block is a child of ref block -> rejected
// after
// choose two blocks with no containment, e.g. two sibling paragraphs
{"action":"swap-block-ref","id":"<paragraphA>","blockID":"<paragraphB>"} Defensive patterns
Strategy: validation
Validate before calling
if (op.id === op.blockID) throw new Error('cannot swap a block with itself');
if (isAncestor(op.id, op.blockID) || isAncestor(op.blockID, op.id)) throw new Error('ancestor/descendant swap not allowed'); Prevention
- Walk each node's parent chain to confirm no containment before swapping
- For parent-child restructuring use move operations, not swaps
When it happens
Trigger: Calling swap-block-ref with operation.ID equal to operation.BlockID; or one block ID being a heading/parent block that contains the other (e.g. swapping a container/parent block with one of its children).
Common situations: Selecting a whole outline/heading section and asking to swap with a block inside it; recursive or scripted swaps where IDs were computed from the same node; UI drag logic passing the drop target's ancestor.
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
- block swap requires two non-document blocks
- cannot swap a heading with a block in its section
- invalid block swap options
- 106
- 199
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/6df5a8d6e2cc622c.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/transaction_block_swap.go:107
if ref == nil || def == nil || ref.Parent == nil || def.Parent == nil {
return errors.New("block swap requires two non-document blocks")
}
if ref.Parent.Type == ast.NodeListItem {
ref = ref.Parent
}
if def.Parent.Type == ast.NodeListItem {
def = def.Parent
}
contains := func(parent, node *ast.Node) bool {
for ; node != nil; node = node.Parent {
if node == parent {
return true
}
}
return false
}
if contains(ref, def) || contains(def, ref) {
return errors.New("cannot swap a block with itself or its ancestor")
}
if includeChildren && def.Type == ast.NodeHeading {
for _, child := range treenode.HeadingChildren(def) {
if contains(child, ref) {
return errors.New("cannot swap a heading with a block in its section")
}
}
}
return nil
}
func captureBlockSwapFragments(trees []*parse.Tree) (ret []blockSwapFragment) {
for _, tree := range trees {
var previousID string
for node := tree.Root.FirstChild; node != nil; node = node.Next {
if !node.IsBlock() || node.ID == "" {
continue
}View on GitHub (pinned to 9f775e8a12)