{"record":{"id":"7faa36945d1a436a","repo":"siyuan-note/siyuan","slug":"block-swap-requires-two-non-document-blocks","errorCode":null,"errorMessage":"block swap requires two non-document blocks","messagePattern":"block swap requires two non-document blocks","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/transaction_block_swap.go","lineNumber":90,"sourceCode":"\tif refTree.ID != defTree.ID {\n\t\ttrees = append(trees, defTree)\n\t}\n\tbefore := captureBlockSwapFragments(trees)\n\ttx.saveBlockSwapOriginalTrees(trees)\n\tswapBlockRefNodes(ref, def, operation.BlockID, *options.IncludeChildren, *options.OriginalToEmbed)\n\tafter := captureBlockSwapFragments(trees)\n\tstate := newBlockSwapState(before, after)\n\toperation.blockSwapState = state\n\toperation.RetData = state.rootIDs\n\ttx.UndoOperations = []*Operation{{Action: \"swapBlockRef\", ID: operation.ID, BlockID: operation.BlockID,\n\t\tData: operation.Data, RetData: state.rootIDs, blockSwapState: state, blockSwapUndo: true}}\n\ttx.finishBlockSwap(state.before, state.after, trees)\n\treturn nil\n}\n\nfunc validateBlockSwap(ref, def *ast.Node, includeChildren bool) error {\n\tif ref == nil || def == nil || ref.Parent == nil || def.Parent == nil {\n\t\treturn errors.New(\"block swap requires two non-document blocks\")\n\t}\n\tif ref.Parent.Type == ast.NodeListItem {\n\t\tref = ref.Parent\n\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}","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/transaction_block_swap.go#L72-L108","documentation":"validateBlockSwap requires both the referencing block and the definition block to be real content blocks with a parent. This error means one of the nodes is nil or is a document root (no parent), which cannot take part in a block swap. Document blocks are the swap boundary and are excluded by design.","triggerScenarios":"Calling swap-block-ref with operation.ID or operation.BlockID pointing to a document ID, to a nonexistent block ID, or to a node fetched without parent linkage.","commonSituations":"Using the document's ID where a block ID was expected; stale IDs left after the target block was deleted; scripts computing IDs from search results that return documents.","solutions":["Pass block IDs (not document IDs) for both operation.ID and operation.BlockID","Verify both blocks still exist (e.g. via /api/query/sql or /api/filetree/getDoc) before swapping","If swapping a list item is intended, pass the item's own ID; the kernel promotes ListItem parents internally"],"exampleFix":"// before\n{\"action\":\"swap-block-ref\",\"id\":\"20240101111111-abc\",\"blockID\":\"20240101-doc-00-root\"} // blockID is a document\n// after\n{\"action\":\"swap-block-ref\",\"id\":\"20240101111111-abc\",\"blockID\":\"20240101222222-xyz\"} // both are child blocks","handlingStrategy":"validation","validationCode":"for (const id of [op.id, op.blockID]) {\n  const node = await getNode(id); // /api/filetree/getDoc or SQL lookup\n  if (!node || node.type === 'document' || node.parentId == null) throw new Error(id + ' is not a non-document block');\n}","typeGuard":"function isSwappableBlock(node) { return node != null && node.type !== 'document' && node.parentId != null; }","tryCatchPattern":null,"preventionTips":["Resolve block IDs from block searches, not document listings","Validate both IDs exist and are non-document blocks before submitting","Handle deleted-block stale IDs in automation scripts"],"tags":["validation","block-swap","api"],"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"}