{"record":{"id":"28779a6710c49bbf","repo":"tursodatabase/turso","slug":"unexpected-cursor-type-in-op-column-range-fetch-bo","errorCode":null,"errorMessage":"unexpected cursor type in op_column_range_fetch body ({other:?})","messagePattern":"unexpected cursor type in op_column_range_fetch body \\((.+?)\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/vdbe/execute.rs","lineNumber":2504,"sourceCode":"                let (left, right) = state.registers.split_at_mut(dest + count);\n                (&mut right[content_reg - (dest + count)], &mut left[dest..])\n            };\n            match content {\n                Register::Record(record) => {\n                    let filled = record\n                        .iter()?\n                        .decode_into_registers_after(start_column, dest_regs)?;\n                    if filled < count {\n                        crate::bail_parse_error!(\"pseudo-cursor column out of range for record\");\n                    }\n                }\n                other => {\n                    crate::bail_parse_error!(\"non-register register in pseudo-record ({other:?})\")\n                }\n            }\n        }\n        other => {\n            panic!(\"unexpected cursor type in op_column_range_fetch body ({other:?})\")\n        }\n    }\n    Ok(InsnFunctionStepResult::Step)\n}\n\npub fn op_column_has_field(\n    program: &Program,\n    state: &mut ProgramState,\n    insn: &Insn,\n    _pager: &Arc<Pager>,\n) -> InsnResult {\n    load_insn!(\n        ColumnHasField {\n            cursor_id,\n            column,\n            target_pc,\n        },\n        insn","sourceCodeStart":2486,"sourceCodeEnd":2522,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/core/vdbe/execute.rs#L2486-L2522","documentation":"op_column_range_fetch (bulk extraction of a column range into registers) matched a cursor kind it cannot handle — the catch-all arm after the B-tree record and pseudo-record cases. The opcode was emitted for a cursor that has no record payload to decode (sorter, virtual table, index-method, etc.), so the program is inconsistent with the cursor's kind.","triggerScenarios":"Plans using the bulk column-range fetch (wide column lists) where the target cursor was allocated as a sorter or vtab cursor — commonly after subquery flattening, or when column-range emission was extended to new statement shapes.","commonSituations":"Wide SELECT lists over many columns; queries over vtabs or sorted subqueries after upgrades enabling the column-range opcode.","solutions":["Report with SQL — the range-fetch opcode must only target record-backed cursors","Narrow the selected column list to see whether the plan falls back to per-column Column opcodes","Move the wide projection into its own query level","Upgrade"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// range fetch requires a record-backed cursor\nif let Insn::ColumnRangeFetch { cursor_id, .. } = insn {\n    match program.cursor_ref.get(*cursor_id).map(|(_, t)| t) {\n        Some(CursorType::BTreeTable(_)) | Some(CursorType::BTreeIndex(_)) | Some(CursorType::Pseudo(_)) => {}\n        other => crate::bail_parse_error!(\"ColumnRangeFetch on non-record cursor ({other:?})\"),\n    }\n}","typeGuard":"fn record_backed(t: &CursorType) -> bool {\n    matches!(t, CursorType::BTreeTable(_) | CursorType::BTreeIndex(_) | CursorType::Pseudo(_))\n}","tryCatchPattern":null,"preventionTips":["Keep wide-projection queries over vtabs/sorters in the regression corpus","Narrow projections when a new engine version enables bulk column fetch","Report with the exact column list that triggers it"],"tags":["vdbe","executor","column","cursor-type","panic","runtime"],"backgroundTag":"cursor-type-mismatch","analyzedSha":"492c4a71cd7c2649e7df83da1471b74f4b1c7aa9","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}