tursodatabase/turso · error
Invalid btree seek state in seek_btree_and_set_peek: {:?}
Error message
Invalid btree seek state in seek_btree_and_set_peek: {:?} What it means
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.
Source
Thrown at core/mvcc/cursor.rs:1135
self.index_finger.reset();
}
/// Seek btree cursor and set btree_peek to the result.
/// Skips rows that are shadowed by MVCC.
/// Returns IOResult indicating if we need to yield for IO or are done.
fn seek_btree_and_set_peek(&mut self, seek_key: SeekKey<'_>, op: SeekOp) -> IOResultOr<()> {
// Fast path: btree not allocated
if !self.is_btree_allocated() {
self.dual_peek.btree_peek = CursorPeek::Exhausted;
self.state = None;
return Ok(IOResult::Done(()));
}
loop {
let Some(MvccLazyCursorState::Seek(SeekState::SeekBtree(btree_seek_state), direction)) =
self.state.clone()
else {
panic!(
"Invalid btree seek state in seek_btree_and_set_peek: {:?}",
self.state
);
};
match btree_seek_state {
SeekBtreeState::SeekBtree => {
let seek_result = return_if_io!(self.btree_cursor.seek(seek_key.clone(), op));
match seek_result {
SeekResult::NotFound => {
self.dual_peek.btree_peek = CursorPeek::Exhausted;
return Ok(IOResult::Done(()));
}
SeekResult::TryAdvance => {
// Need to advance to find actual matching entry
self.state.replace(MvccLazyCursorState::Seek(
SeekState::SeekBtree(SeekBtreeState::AdvanceBTree),
direction,View on GitHub (pinned to 492c4a71cd)
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
Defensive patterns
Strategy: validation
Validate before calling
debug_assert!(
matches!(self.state, Some(MvccLazyCursorState::Seek(SeekState::SeekBtree(_), _))),
"seek state corrupted before btree seek continuation"
); Prevention
- 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
When it happens
Trigger: 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.
Common situations: Re-entrancy after async IO suspension inside MVCC seeks; contributor changes to SeekState transitions.
Related errors
- invalid position to read current mvcc row
- SeekKey::IndexKey requires Index cursor type
- RowKey::Record requires Index cursor type
- Cursor not found: {key:?}
- DB should not be initialized and should not do any IO
AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-08-20).
Data as JSON: /api/errors/2348772e6e5464ef.
Report an issue: GitHub.