tursodatabase/turso · error
Rewind on non-btree/materialized-view cursor
Error message
Rewind on non-btree/materialized-view cursor
What it means
Insn::Rewind (position a scan cursor at its first row) executed on a cursor that is neither a BTree nor a MaterializedView runtime cursor. Rewind only works on B-tree-backed objects; sorter, pseudo, and virtual table cursors use different opcodes (SorterRewind, VFilter/VNext). The program advanced a cursor of the wrong kind — a codegen bug caught mid-execution in op_rewind.
Source
Thrown at core/vdbe/execute.rs:1786
);
assert!(pc_if_empty.is_offset());
// Clear any bloom filter associated with this cursor so stale filter data
// does not incorrectly reject valid matches in subsequent iterations.
if let Some(filter) = state.get_bloom_filter_mut(*cursor_id) {
filter.clear();
}
let is_empty = {
let cursor = state.get_cursor(*cursor_id);
match cursor {
Cursor::BTree(btree_cursor) => {
return_if_io!(btree_cursor.rewind());
btree_cursor.is_empty()
}
Cursor::MaterializedView(mv_cursor) => {
return_if_io!(mv_cursor.rewind());
!mv_cursor.is_valid()?
}
_ => panic!("Rewind on non-btree/materialized-view cursor"),
}
};
if is_empty {
state.pc = pc_if_empty.as_offset_int();
} else {
// Rewind positions to the first row, which is effectively a read
state.record_rows_read(1);
state.pc += 1;
}
Ok(InsnFunctionStepResult::Step)
}
pub fn op_last(
program: &Program,
state: &mut ProgramState,
insn: &Insn,
_pager: &Arc<Pager>,
) -> InsnResult {View on GitHub (pinned to 6c72522679)
Solutions
- Report the SQL — Rewind must only target BTreeTable/BTreeIndex/MaterializedView cursors
- Confirm the shape by removing ORDER BY / DISTINCT / vtab references one at a time to find which cursor kind is mis-targeted
- Restructure so the suspicious construct sits in its own query level
- Upgrade
Defensive patterns
Strategy: validation
Validate before calling
// Rewind must target btree-backed cursors only
for insn in &program.insns {
if let Insn::Rewind { cursor_id, .. } = insn {
match program.cursor_ref.get(*cursor_id).map(|(_, t)| t) {
Some(CursorType::BTreeTable(_)) | Some(CursorType::BTreeIndex(_))
| Some(CursorType::MaterializedView(..)) => {}
other => crate::bail_parse_error!("Rewind on non-btree cursor ({other:?})"),
}
}
} Prevention
- Include ORDER BY/DISTINCT/vtab plus subquery mixes in the CI corpus
- Debug-build program audits for loop opcodes vs cursor kinds
- Boundary catch_unwind to convert engine panics into query failures
When it happens
Trigger: Full-scan emission (Rewind) resolved to a cursor that was actually allocated as a sorter (ORDER BY pipeline), pseudo (VALUES/co-routine), or virtual table cursor — typically after cursor id reuse across subquery flattening or changes to which opcode the loop emitter picks.
Common situations: Queries mixing ORDER BY/DISTINCT with flattened subqueries; engine upgrades changing flattening or loop-emission rules.
Related errors
- OpenRead on pseudo cursor
- unexpected cursor type
- Next on non-btree/materialized-view cursor
- {} on unexpected cursor
- OpenRead on sorter cursor
AI-assisted analysis of tursodatabase/turso@6c72522679 (2026-08-20).
Data as JSON: /api/errors/77681417479ac661.
Report an issue: GitHub.