tursodatabase/turso · error

OpenRead on sorter cursor

Error message

OpenRead on sorter cursor

What it means

Insn::OpenRead executed on a cursor whose type is CursorType::Sorter. Sorter cursors are ephemeral row stores filled by SorterInsert for ORDER BY / GROUP BY / DISTINCT processing and are never backed by a database B-tree, so OpenRead is meaningless on them. The bytecode program used a sorter cursor id where a B-tree cursor was intended — a codegen inconsistency, panicked inside op_open_read.

Source

Thrown at core/vdbe/execute.rs:1451

                num_columns,
            )?;
            let index_info = Arc::new(if let Some(mv_store) = mv_store.as_ref() {
                IndexInfo::new_from_index_in(index, mv_store.allocator())?
            } else {
                IndexInfo::new_from_index(index)?
            });
            let cursor =
                maybe_promote_to_mvcc_cursor(btree_cursor, MvccCursorType::Index(index_info))?;
            cursors
                .get_mut(*cursor_id)
                .expect("cursor_id should be valid")
                .replace(cursor.into_cursor());
        }
        CursorType::Pseudo(_) => {
            panic!("OpenRead on pseudo cursor");
        }
        CursorType::Sorter => {
            panic!("OpenRead on sorter cursor");
        }
        CursorType::IndexMethod(..) => {
            unreachable!("IndexMethod handled above")
        }
        CursorType::VirtualTable(_) => {
            panic!("OpenRead on virtual table cursor, use Insn:VOpen instead");
        }
    }
    state.pc += 1;
    Ok(InsnFunctionStepResult::Step)
}

pub fn op_vopen(
    program: &Program,
    state: &mut ProgramState,
    insn: &Insn,
    _pager: &Arc<Pager>,
) -> InsnResult {

View on GitHub (pinned to 492c4a71cd)

Solutions

  1. Report the SQL — the sorter cursor id must not flow into an OpenRead emission
  2. Confirm the trigger by removing ORDER BY / DISTINCT and re-running; then attach both plans to the bug
  3. Rewrite the query to sort in an outer query level so sorter and table cursors do not mix
  4. Upgrade
Defensive patterns

Strategy: validation

Validate before calling

// reuse the program audit: flag OpenRead against sorter cursors before execution
for insn in &program.insns {
    if let Insn::OpenRead { cursor_id, .. } = insn {
        if matches!(program.cursor_ref.get(*cursor_id).map(|(_, t)| t), Some(CursorType::Sorter)) {
            crate::bail_parse_error!("OpenRead targets sorter cursor");
        }
    }
}

Prevention

When it happens

Trigger: ORDER BY / GROUP BY / DISTINCT queries whose sorter cursor id is reused or mis-resolved by a later OpenRead emission; cursor renumbering changes; scalar subqueries inside sorted outer queries where sorter and table cursor ids cross.

Common situations: Queries combining ORDER BY or DISTINCT with joins/subqueries; engine upgrades or planner changes that reallocated sorter cursor ids.

Related errors


AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-08-20). Data as JSON: /api/errors/66fe727752415292. Report an issue: GitHub.