{"record":{"id":"f96093dfdc3a6d5b","repo":"tursodatabase/turso","slug":"post-balancing-seek-key-should-be-some","errorCode":null,"errorMessage":"post_balancing_seek_key should be Some","messagePattern":"post_balancing_seek_key should be Some","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"core/storage/btree.rs","lineNumber":7177,"sourceCode":"                    }) = self.state\n                    else {\n                        unreachable!(\"expected check needs balancing state\");\n                    };\n\n                    if needs_balancing {\n                        let balance_only_ancestor =\n                            !leaf_underflows && interior_overflows_or_underflows;\n                        if balance_only_ancestor {\n                            // Only need to balance the ancestor page; move there immediately.\n                            while self.stack.current() > btree_depth {\n                                self.stack.pop();\n                            }\n                        }\n                        let balance_both = leaf_underflows && interior_overflows_or_underflows;\n                        turso_assert!(matches!(self.balance_state.sub_state, BalanceSubState::Start), \"no balancing operation should be in progress during delete\", { \"sub_state\": self.balance_state.sub_state });\n                        let post_balancing_seek_key = post_balancing_seek_key\n                            .take()\n                            .expect(\"post_balancing_seek_key should be Some\");\n                        self.save_context(post_balancing_seek_key);\n                        self.state = CursorState::Delete(DeleteState::Balancing {\n                            balance_ancestor_at_depth: if balance_both {\n                                Some(btree_depth)\n                            } else {\n                                None\n                            },\n                        });\n                    } else {\n                        // No balancing needed.\n                        if interior_node_was_replaced {\n                            // If we did replace an interior node, we need to advance the cursor once to\n                            // get back at the interior node that now has the replaced content.\n                            // The reason it is important to land here is that the replaced cell was smaller (LT) than the deleted cell,\n                            // so we must ensure we skip over it. I.e., when BTreeCursor::next() is called, it will move past the cell\n                            // that holds the replaced content.\n                            self.state =\n                                CursorState::Delete(DeleteState::PostInteriorNodeReplacement);","sourceCodeStart":7159,"sourceCodeEnd":7195,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/core/storage/btree.rs#L7159-L7195","documentation":"When a delete decides rebalancing is needed, it takes post_balancing_seek_key - the key saved at delete start (DeleteState::LoadPage carries Some(target_key)) so the cursor can re-position after balancing. post_balancing_seek_key.take().expect(\"post_balancing_seek_key should be Some\") fires when that Option was already consumed or was never set on this path.","triggerScenarios":"A delete whose leaf or interior page under/overflows (triggering DeleteState::Balancing) after the seek key was already taken by an earlier state transition, or a delete initiated without a target key so the Option was None from the start.","commonSituations":"Repeated deletes reusing one cursor where a duplicate take() races IO re-entry, deletes driven by UPDATE on indexed columns, engine regressions in DeleteState field movement (post_balancing_seek_key.take() chains).","solutions":["Report to Turso with the statement sequence - double-take or missing key in the delete state machine","Retry with a freshly prepared DELETE statement and cursor","Run PRAGMA integrity_check after the panic to confirm the tree is intact","Upgrade the engine version"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let result = std::panic::catch_unwind(AssertUnwindSafe(|| {\n    conn.execute(\"DELETE FROM t WHERE rowid = ?\", [rowid])\n}));\nif result.is_err() { conn.close().ok(); /* reopen and retry once */ }","preventionTips":["Prepare a new statement per logical delete batch rather than re-binding one cursor for many deletes","Keep engine version current; DeleteState key plumbing sees frequent fixes","Wrap deletes in transactions to define clean retry boundaries","Report double-take style repros upstream"],"tags":["btree","delete","balancing","state-machine","panic"],"backgroundTag":"invalid-cursor-state","analyzedSha":"492c4a71cd7c2649e7df83da1471b74f4b1c7aa9","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}