{"record":{"id":"92c904bc92e55d41","repo":"tursodatabase/turso","slug":"next-on-non-btree-materialized-view-cursor","errorCode":null,"errorMessage":"Next on non-btree/materialized-view cursor","messagePattern":"Next on non-btree/materialized-view cursor","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/vdbe/execute.rs","lineNumber":3382,"sourceCode":"                let is_null_row = btree_cursor.get_null_flag();\n                btree_cursor.set_null_flag(false);\n                if is_null_row {\n                    true // is_empty = true\n                } else {\n                    return_if_io!(btree_cursor.next());\n                    btree_cursor.is_empty()\n                }\n            }\n            Cursor::MaterializedView(mv_cursor) => {\n                let has_more = return_if_io!(mv_cursor.next());\n                !has_more\n            }\n            Cursor::IndexMethod(_) => {\n                let cursor = cursor.as_index_method_mut();\n                let has_more = return_if_io!(cursor.query_next());\n                !has_more\n            }\n            _ => panic!(\"Next on non-btree/materialized-view cursor\"),\n        }\n    };\n    if !is_empty {\n        // Increment metrics for row read\n        state.record_rows_read(1);\n        state.metrics.btree_next = state.metrics.btree_next.saturating_add(1);\n        state.metrics.search_count = state.metrics.search_count.saturating_add(1);\n        // Only steps codegen marked as part of a full table scan count as\n        // fullscan steps, matching SQLITE_STMTSTATUS_FULLSCAN_STEP.\n        if *fullscan {\n            state.metrics.fullscan_steps = state.metrics.fullscan_steps.saturating_add(1);\n        }\n        if let Some((_, cursor_type)) = program.cursor_ref.get(*cursor_id) {\n            if cursor_type.is_index() {\n                state.metrics.index_steps = state.metrics.index_steps.saturating_add(1);\n            }\n        }\n        state.pc = pc_if_next.as_offset_int();","sourceCodeStart":3364,"sourceCodeEnd":3400,"githubUrl":"https://github.com/tursodatabase/turso/blob/6c7252267988c76e632af00a671e4b9788dfae13/core/vdbe/execute.rs#L3364-L3400","documentation":"Insn::Next (advance a scan cursor to the following row) executed on a cursor outside the handled set — the match covers BTree, MaterializedView, and IndexMethod; sorter, pseudo, and virtual table cursors are not (sorters advance via SorterNext, vtabs via VNext). The program advanced a cursor with the wrong opcode — a codegen inconsistency caught in op_next.","triggerScenarios":"Loop emission picking the generic Next for a cursor allocated as a sorter (ORDER BY/GROUP BY pipeline), pseudo, or virtual table — e.g. after flattening rules changed or DISTINCT handling was reworked, so the loop opcode no longer matches the cursor kind.","commonSituations":"ORDER BY / GROUP BY / DISTINCT queries mixed with joins and subqueries; engine upgrades altering loop emission.","solutions":["Report the SQL — the loop's advance opcode must match the cursor kind","Test removing ORDER BY / GROUP BY / DISTINCT and vtab references to isolate which cursor is mis-targeted","Restructure so sorting happens in an outer query level","Upgrade"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Next must only advance cursors that support it\nfor insn in &program.insns {\n    if let Insn::Next { cursor_id, .. } = insn {\n        match program.cursor_ref.get(*cursor_id).map(|(_, t)| t) {\n            Some(CursorType::BTreeTable(_)) | Some(CursorType::BTreeIndex(_))\n            | Some(CursorType::MaterializedView(..)) | Some(CursorType::IndexMethod(..)) => {}\n            other => crate::bail_parse_error!(\"Next on unsupported cursor ({other:?})\"),\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Cover ORDER BY/GROUP BY/DISTINCT with joins and subqueries in CI","Debug-build audits matching loop opcodes to cursor kinds","Boundary catch_unwind for engine panics"],"tags":["vdbe","executor","cursor-type","iteration","panic","runtime"],"backgroundTag":"bytecode-cursor-type-mismatch","analyzedSha":"6c7252267988c76e632af00a671e4b9788dfae13","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}