{"record":{"id":"d616c02062327cae","repo":"siyuan-note/siyuan","slug":"block-swap-id-already-exists-s","errorCode":null,"errorMessage":"block swap ID already exists: %s","messagePattern":"block swap ID already exists: (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/transaction_block_swap.go","lineNumber":267,"sourceCode":"\t\tfor _, id := range node.BlockIDs() {\n\t\t\tremoving[id] = true\n\t\t}\n\t}\n\tavailable := map[string]bool{}\n\tfor _, fragment := range to {\n\t\ttree := trees[fragment.rootID]\n\t\tif tree == nil || tree.Box != fragment.boxID {\n\t\t\treturn errors.New(\"block swap notebook has changed\")\n\t\t}\n\t\tif fragment.previousID != \"\" && !available[fragment.previousID] {\n\t\t\tprevious := treenode.GetNodeInTree(tree, fragment.previousID)\n\t\t\tif previous == nil || previous.Parent != tree.Root || removing[previous.ID] {\n\t\t\t\treturn errors.New(\"block swap insertion position has changed\")\n\t\t\t}\n\t\t}\n\t\tfor _, id := range fragment.node.BlockIDs() {\n\t\t\tif existing := treenode.GetBlockTreeInBox(id, fragment.boxID); existing != nil && !removing[id] {\n\t\t\t\treturn fmt.Errorf(\"block swap ID already exists: %s\", id)\n\t\t\t}\n\t\t\tfor _, currentTree := range trees {\n\t\t\t\tif treenode.GetNodeInTree(currentTree, id) != nil && !removing[id] {\n\t\t\t\t\treturn fmt.Errorf(\"block swap ID already exists: %s\", id)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tavailable[fragment.node.ID] = true\n\t}\n\tfor _, fragment := range from {\n\t\ttreenode.GetNodeInTree(trees[fragment.rootID], fragment.node.ID).Unlink()\n\t}\n\tfor _, fragment := range to {\n\t\ttree := trees[fragment.rootID]\n\t\tnode := cloneBlockSwapNode(fragment.node)\n\t\tif fragment.previousID == \"\" {\n\t\t\ttree.Root.PrependChild(node)\n\t\t} else {","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/transaction_block_swap.go#L249-L285","documentation":"Before inserting 'to' fragments, the replay checks that no block ID carried by the fragment already exists in the notebook's block-tree index (treenode.GetBlockTreeInBox), unless that ID is being removed by the same swap. A surviving duplicate means the fragment's IDs were taken by another block (e.g. via duplicate-doc or sync divergence), and inserting would create duplicate block IDs — illegal in SiYuan — so the replay aborts naming the conflicting ID.","triggerScenarios":"replayBlockSwap -> restoreBlockSwapFragments: one of fragment.node.BlockIDs() resolves via treenode.GetBlockTreeInBox(id, fragment.boxID) to an existing block-tree row that is not in the removing set — i.e. the ID is live in the same notebook outside the blocks this swap removes.","commonSituations":"Duplicating a document creates blocks with copied IDs that collide with pending swap fragments; sync divergence duplicated content across devices; a partially applied earlier transaction left the fragment IDs in the index; the swap was replayed twice.","solutions":["Search the notebook for the conflicting block ID in the message and delete or re-id the duplicate block","Ensure the block swap is not replayed twice; clear duplicate/stale pending transactions","Run a reindex (Reindex) to reconcile the block-tree index with actual .sy files, then retry","Sync and resolve document duplication conflicts before replaying the swap"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Go: detect live IDs before replay\nfor _, id := range fragment.node.BlockIDs() {\n    if existing := treenode.GetBlockTreeInBox(id, fragment.boxID); existing != nil && !removing[id] {\n        return fmt.Errorf(\"block swap ID already exists: %s\", id)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never replay a block-swap transaction twice; track applied transaction IDs","Resolve document-duplication conflicts from sync before replaying swaps","Reindex the notebook if the block index may be stale relative to .sy files"],"tags":["go","transaction","block-swap","duplicate-id"],"backgroundTag":"conflicting-identifier","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"}