{"record":{"id":"c2add30510fe8582","repo":"siyuan-note/siyuan","slug":"block-swap-undo-state-is-unavailable","errorCode":null,"errorMessage":"block swap undo state is unavailable","messagePattern":"block swap undo state is unavailable","errorType":"exception","errorClass":"TxErr","httpStatus":null,"severity":"error","filePath":"kernel/model/transaction_block_swap.go","lineNumber":40,"sourceCode":"\tbefore, after []blockSwapFragment\n\trootIDs       []string\n}\n\ntype blockSwapFragment struct {\n\trootID, boxID, previousID, nextID string\n\tnode                              *ast.Node\n}\n\nfunc (tx *Transaction) doSwapBlockRef(operation *Operation) *TxErr {\n\tfail := func(err error) *TxErr {\n\t\treturn &TxErr{code: TxErrCodePushMsg, id: operation.ID, msg: err.Error()}\n\t}\n\tif len(tx.DoOperations) != 1 {\n\t\treturn fail(errors.New(\"block swap must be submitted as a separate transaction\"))\n\t}\n\tif tx.isReplay {\n\t\tif operation.blockSwapState == nil {\n\t\t\treturn fail(errors.New(\"block swap undo state is unavailable\"))\n\t\t}\n\t\tif err := tx.replayBlockSwap(operation); err != nil {\n\t\t\treturn fail(err)\n\t\t}\n\t\treturn nil\n\t}\n\tdata, err := json.Marshal(operation.Data)\n\tif err != nil {\n\t\treturn fail(err)\n\t}\n\tvar options blockSwapOptions\n\tif err = json.Unmarshal(data, &options); err != nil || options.IncludeChildren == nil || options.OriginalToEmbed == nil {\n\t\treturn fail(errors.New(\"invalid block swap options\"))\n\t}\n\trefTree, err := tx.loadTree(operation.ID)\n\tif err != nil {\n\t\treturn fail(err)\n\t}","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/transaction_block_swap.go#L22-L58","documentation":"During transaction replay (undo/redo), the kernel needs the stored blockSwapState snapshot attached to the swap operation to reconstruct the original trees. This error means the replay is attempting a swap operation whose undo state is missing. It indicates internal state loss rather than a user input problem.","triggerScenarios":"Replaying (undo/redo) a transaction whose swap-block-ref operation lacks operation.blockSwapState; state persisted before this feature existed being replayed by a newer kernel.","commonSituations":"Workspace data or sync snapshots from older versions replayed after an upgrade; corrupted transaction journal; custom code stripping operation metadata before replay.","solutions":["Reload the workspace so the transaction state is reloaded or discarded","Check for version mismatch between synced clients and upgrade all clients","If reproducible, report a bug: replay state should always be attached by doSwapBlockRef"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (op.action === 'swap-block-ref' && op.isReplay && op.blockSwapState == null) {\n  // cannot replay; fall back to re-executing or skipping\n}","typeGuard":"function hasSwapState(op) { return op != null && op.blockSwapState != null; }","tryCatchPattern":"try { await post('/api/transactions', tx); } catch (e) {\n  if (e.message.includes('block swap undo state is unavailable')) { /* skip or re-execute op */ }\n}","preventionTips":["Do not strip custom operation metadata when serializing transactions","Keep all sync clients on a kernel version that supports block swap state"],"tags":["transactions","undo","replay","block-swap"],"backgroundTag":"internal-invariant-violation","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"}