{"record":{"id":"66fe727752415292","repo":"tursodatabase/turso","slug":"openread-on-sorter-cursor","errorCode":null,"errorMessage":"OpenRead on sorter cursor","messagePattern":"OpenRead on sorter cursor","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/vdbe/execute.rs","lineNumber":1451,"sourceCode":"                num_columns,\n            )?;\n            let index_info = Arc::new(if let Some(mv_store) = mv_store.as_ref() {\n                IndexInfo::new_from_index_in(index, mv_store.allocator())?\n            } else {\n                IndexInfo::new_from_index(index)?\n            });\n            let cursor =\n                maybe_promote_to_mvcc_cursor(btree_cursor, MvccCursorType::Index(index_info))?;\n            cursors\n                .get_mut(*cursor_id)\n                .expect(\"cursor_id should be valid\")\n                .replace(cursor.into_cursor());\n        }\n        CursorType::Pseudo(_) => {\n            panic!(\"OpenRead on pseudo cursor\");\n        }\n        CursorType::Sorter => {\n            panic!(\"OpenRead on sorter cursor\");\n        }\n        CursorType::IndexMethod(..) => {\n            unreachable!(\"IndexMethod handled above\")\n        }\n        CursorType::VirtualTable(_) => {\n            panic!(\"OpenRead on virtual table cursor, use Insn:VOpen instead\");\n        }\n    }\n    state.pc += 1;\n    Ok(InsnFunctionStepResult::Step)\n}\n\npub fn op_vopen(\n    program: &Program,\n    state: &mut ProgramState,\n    insn: &Insn,\n    _pager: &Arc<Pager>,\n) -> InsnResult {","sourceCodeStart":1433,"sourceCodeEnd":1469,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/core/vdbe/execute.rs#L1433-L1469","documentation":"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.","triggerScenarios":"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.","commonSituations":"Queries combining ORDER BY or DISTINCT with joins/subqueries; engine upgrades or planner changes that reallocated sorter cursor ids.","solutions":["Report the SQL — the sorter cursor id must not flow into an OpenRead emission","Confirm the trigger by removing ORDER BY / DISTINCT and re-running; then attach both plans to the bug","Rewrite the query to sort in an outer query level so sorter and table cursors do not mix","Upgrade"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// reuse the program audit: flag OpenRead against sorter cursors before execution\nfor insn in &program.insns {\n    if let Insn::OpenRead { cursor_id, .. } = insn {\n        if matches!(program.cursor_ref.get(*cursor_id).map(|(_, t)| t), Some(CursorType::Sorter)) {\n            crate::bail_parse_error!(\"OpenRead targets sorter cursor\");\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Include ORDER BY/DISTINCT plus join/subquery combinations in CI query corpora","Audit emitted programs for OpenRead on sorter cursors in debug builds","Catch engine panics per-statement at the embedding boundary"],"tags":["vdbe","executor","cursor-type","sorter","order-by","panic","runtime"],"backgroundTag":"bytecode-cursor-type-mismatch","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"}