{"record":{"id":"2348772e6e5464ef","repo":"tursodatabase/turso","slug":"invalid-btree-seek-state-in-seek-btree-and-set-pee","errorCode":null,"errorMessage":"Invalid btree seek state in seek_btree_and_set_peek: {:?}","messagePattern":"Invalid btree seek state in seek_btree_and_set_peek: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/mvcc/cursor.rs","lineNumber":1135,"sourceCode":"        self.index_finger.reset();\n    }\n\n    /// Seek btree cursor and set btree_peek to the result.\n    /// Skips rows that are shadowed by MVCC.\n    /// Returns IOResult indicating if we need to yield for IO or are done.\n    fn seek_btree_and_set_peek(&mut self, seek_key: SeekKey<'_>, op: SeekOp) -> IOResultOr<()> {\n        // Fast path: btree not allocated\n        if !self.is_btree_allocated() {\n            self.dual_peek.btree_peek = CursorPeek::Exhausted;\n            self.state = None;\n            return Ok(IOResult::Done(()));\n        }\n\n        loop {\n            let Some(MvccLazyCursorState::Seek(SeekState::SeekBtree(btree_seek_state), direction)) =\n                self.state.clone()\n            else {\n                panic!(\n                    \"Invalid btree seek state in seek_btree_and_set_peek: {:?}\",\n                    self.state\n                );\n            };\n            match btree_seek_state {\n                SeekBtreeState::SeekBtree => {\n                    let seek_result = return_if_io!(self.btree_cursor.seek(seek_key.clone(), op));\n\n                    match seek_result {\n                        SeekResult::NotFound => {\n                            self.dual_peek.btree_peek = CursorPeek::Exhausted;\n                            return Ok(IOResult::Done(()));\n                        }\n                        SeekResult::TryAdvance => {\n                            // Need to advance to find actual matching entry\n                            self.state.replace(MvccLazyCursorState::Seek(\n                                SeekState::SeekBtree(SeekBtreeState::AdvanceBTree),\n                                direction,","sourceCodeStart":1117,"sourceCodeEnd":1153,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/core/mvcc/cursor.rs#L1117-L1153","documentation":"seek_btree_and_set_peek drives the btree half of the dual (btree + MVCC) seek in a loop that requires self.state to be Seek(SeekBtree(..), direction) at the top of every iteration. Any other state on entry or after a mutation means the state machine advanced wrongly, and it panics with the state dumped for diagnosis.","triggerScenarios":"Internal: seek code paths that mutate self.state mid-seek (IO suspension and re-entry via IOResult::IO, rewinds, direction switches) leaving a non-SeekBtree state while the loop continues.","commonSituations":"Re-entrancy after async IO suspension inside MVCC seeks; contributor changes to SeekState transitions.","solutions":["If contributing: verify every IO suspension inside the loop restores Seek(SeekBtree(..), dir) before re-entering","Reduce to a minimal query using an index under MVCC and report, including the panic state dump","Avoid the specific seek pattern (mixed ASC/DESC index scans) until fixed"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"debug_assert!(\n    matches!(self.state, Some(MvccLazyCursorState::Seek(SeekState::SeekBtree(_), _))),\n    \"seek state corrupted before btree seek continuation\"\n);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["After any IOResult::IO suspension, restore the exact Seek(SeekBtree(..), dir) state before re-entering the loop","Add seek-state assertions to tests that interleave IO yields under MVCC"],"tags":["mvcc","cursor","seek","state-machine","internal","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"}