siyuan-note/siyuan · error
cannot swap a heading with a block in its section
Error message
cannot swap a heading with a block in its section
What it means
When IncludeChildren is enabled and the definition block is a heading, the swap is refused if the reference block lies anywhere inside that heading's section (the heading plus its following child blocks until the next heading). Swapping would tear the section apart, so HeadingChildren is scanned with contains() to reject it.
Solutions
- Disable includeChildren in the swap options if the section grouping should not apply
- Swap the ref block with a block outside the heading's section
- Move the ref block out of the heading's section first, then perform the swap
Example fix
// before
data: {"includeChildren":true,"originalToEmbed":false} // ref lies inside def heading's section
// after
data: {"includeChildren":false,"originalToEmbed":false} // or choose a ref outside the section Defensive patterns
Strategy: validation
Validate before calling
if (options.includeChildren && isHeading(defBlock)) {
const section = headingChildrenIds(defBlock); // blocks under heading until next heading
if (section.includes(op.id)) throw new Error('ref block lies inside the heading section');
} Type guard
function isHeading(node) { return node != null && node.type === 'h'; } Prevention
- When includeChildren is on, ensure the ref block is outside any heading section of the def block
- Or set includeChildren=false when section grouping is not required
When it happens
Trigger: Calling swap-block-ref with originalToEmbed/includeChildren semantics where def is a NodeHeading and ref (operation.ID) is one of the blocks under that heading's section.
Common situations: Swapping a paragraph that sits below a heading with that heading itself while 'include children' is on; outline-level operations that treat a heading and its section as one unit.
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 block with itself or its ancestor
- invalid block swap options
- invalid heading conversion parameters
- invalid heading fold scope
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/0b0558dcd4593ef1.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/transaction_block_swap.go:112
}
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
}
fragment := blockSwapFragment{rootID: tree.ID, boxID: tree.Box, previousID: previousID, node: cloneBlockSwapNode(node)}
for next := node.Next; next != nil; next = next.Next {
if next.IsBlock() && next.ID != "" {
fragment.nextID = next.ID
breakView on GitHub (pinned to 9f775e8a12)