{"record":{"id":"0b0558dcd4593ef1","repo":"siyuan-note/siyuan","slug":"cannot-swap-a-heading-with-a-block-in-its-section","errorCode":null,"errorMessage":"cannot swap a heading with a block in its section","messagePattern":"cannot swap a heading with a block in its section","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/transaction_block_swap.go","lineNumber":112,"sourceCode":"\t}\n\tif def.Parent.Type == ast.NodeListItem {\n\t\tdef = def.Parent\n\t}\n\tcontains := func(parent, node *ast.Node) bool {\n\t\tfor ; node != nil; node = node.Parent {\n\t\t\tif node == parent {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\tif contains(ref, def) || contains(def, ref) {\n\t\treturn errors.New(\"cannot swap a block with itself or its ancestor\")\n\t}\n\tif includeChildren && def.Type == ast.NodeHeading {\n\t\tfor _, child := range treenode.HeadingChildren(def) {\n\t\t\tif contains(child, ref) {\n\t\t\t\treturn errors.New(\"cannot swap a heading with a block in its section\")\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc captureBlockSwapFragments(trees []*parse.Tree) (ret []blockSwapFragment) {\n\tfor _, tree := range trees {\n\t\tvar previousID string\n\t\tfor node := tree.Root.FirstChild; node != nil; node = node.Next {\n\t\t\tif !node.IsBlock() || node.ID == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfragment := blockSwapFragment{rootID: tree.ID, boxID: tree.Box, previousID: previousID, node: cloneBlockSwapNode(node)}\n\t\t\tfor next := node.Next; next != nil; next = next.Next {\n\t\t\t\tif next.IsBlock() && next.ID != \"\" {\n\t\t\t\t\tfragment.nextID = next.ID\n\t\t\t\t\tbreak","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/transaction_block_swap.go#L94-L130","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ndata: {\"includeChildren\":true,\"originalToEmbed\":false} // ref lies inside def heading's section\n// after\ndata: {\"includeChildren\":false,\"originalToEmbed\":false} // or choose a ref outside the section","handlingStrategy":"validation","validationCode":"if (options.includeChildren && isHeading(defBlock)) {\n  const section = headingChildrenIds(defBlock); // blocks under heading until next heading\n  if (section.includes(op.id)) throw new Error('ref block lies inside the heading section');\n}","typeGuard":"function isHeading(node) { return node != null && node.type === 'h'; }","tryCatchPattern":null,"preventionTips":["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"],"tags":["validation","block-swap","heading"],"backgroundTag":"invalid-argument-value","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}