{"record":{"id":"bad167c052756a9e","repo":"tursodatabase/turso","slug":"openread-on-virtual-table-cursor-use-insn-vopen-i","errorCode":null,"errorMessage":"OpenRead on virtual table cursor, use Insn:VOpen instead","messagePattern":"OpenRead on virtual table cursor, use Insn:VOpen instead","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/vdbe/execute.rs","lineNumber":1457,"sourceCode":"            });\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 {\n    load_insn!(VOpen { cursor_id }, insn);\n    let (_, cursor_type) = program\n        .cursor_ref\n        .get(*cursor_id)\n        .expect(\"cursor_id should exist in cursor_ref\");\n    let CursorType::VirtualTable(virtual_table) = cursor_type else {","sourceCodeStart":1439,"sourceCodeEnd":1475,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/core/vdbe/execute.rs#L1439-L1475","documentation":"Insn::OpenRead executed on a CursorType::VirtualTable cursor. Virtual tables are opened with Insn::VOpen, which calls the module's open callback to create a module cursor; they have no root page for a B-tree OpenRead. If translation emits OpenRead for a vtab cursor, the virtual table was not detected in that code path and the program is malformed.","triggerScenarios":"Statements over virtual tables (csv, regexp, fts, pragma-backed vtabs like pragma_table_info, dbstat/dbpage) where a code path — upsert, RETURNING, trigger body, UPDATE ... FROM — fell back to generic table-cursor emission and missed the is_virtual check.","commonSituations":"Using bundled extensions and catalog-style vtabs in complex DML; engine versions where virtual-table detection in translate/ changed; shadow tables accessed directly.","solutions":["Report with the virtual table module and full SQL — translation must emit VOpen/VColumn/VUpdate for vtab cursors","Validate the statement against sqlite3 to confirm it is legal, then reduce it (drop RETURNING / ON CONFLICT / trigger parts) to isolate the path missing vtab handling","Stage access through a plain SELECT from the vtab into a temp table before doing complex operations","Upgrade"],"exampleFix":"// contributor fix in translate/: open vtab cursors with VOpen, not OpenRead\nif table.is_virtual() {\n    program.emit_insn(Insn::VOpen { cursor_id });\n} else {\n    program.emit_insn(Insn::OpenRead { cursor_id, root_page, .. });\n}","handlingStrategy":"validation","validationCode":"// OpenRead must never target a virtual table cursor\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::VirtualTable(_))) {\n            crate::bail_parse_error!(\"OpenRead on virtual table cursor; VOpen required\");\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Maintain conformance coverage for every vtab extension under SELECT/INSERT/UPDATE/DELETE plus RETURNING","Audit emitted programs for OpenRead targeting VirtualTable cursors in debug builds","Validate new statements against sqlite3 before enabling them in production paths"],"tags":["vdbe","executor","virtual-table","cursor-type","panic","runtime"],"backgroundTag":"virtual-table-instruction-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"}