{"record":{"id":"a7d1a1bc0deca499","repo":"tursodatabase/turso","slug":"cursor-not-found-cursor-id","errorCode":null,"errorMessage":"Cursor not found: {cursor_id}","messagePattern":"Cursor not found: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/vdbe/builder.rs","lineNumber":1886,"sourceCode":"            .unwrap_or_else(|| panic!(\"No index cursor found for table {table_ref_id}\"))\n    }\n\n    pub fn resolve_any_index_cursor_id_for_table_safe(\n        &self,\n        table_ref_id: TableInternalId,\n    ) -> Option<CursorID> {\n        self.cursor_ref.iter().position(|(k, _)| {\n            k.as_ref()\n                .is_some_and(|k| k.table_reference_id == table_ref_id && k.index.is_some())\n        })\n    }\n\n    /// Resolve the [Index] that a given cursor is associated with.\n    pub fn resolve_index_for_cursor_id(&self, cursor_id: CursorID) -> Arc<Index> {\n        let cursor_ref = &self\n            .cursor_ref\n            .get(cursor_id)\n            .unwrap_or_else(|| panic!(\"Cursor not found: {cursor_id}\"))\n            .1;\n        let CursorType::BTreeIndex(index) = cursor_ref else {\n            panic!(\"Cursor is not an index: {cursor_id}\");\n        };\n        index.clone()\n    }\n\n    /// Get the [CursorType] of a given cursor.\n    pub fn get_cursor_type(&self, cursor_id: CursorID) -> Option<&CursorType> {\n        self.cursor_ref\n            .get(cursor_id)\n            .map(|(_, cursor_type)| cursor_type)\n    }\n\n    pub const fn set_collation(&mut self, c: Option<(CollationSeq, bool)>) {\n        self.collation = c\n    }\n","sourceCodeStart":1868,"sourceCodeEnd":1904,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/core/vdbe/builder.rs#L1868-L1904","documentation":"resolve_index_for_cursor_id() maps a CursorID to the Arc<Index> of the index it scans; the panic fires when cursor_id does not index into the program builder's cursor_ref vector at all. Translation referenced a cursor slot that was never allocated by an OpenRead/cursor-allocation step. This is a bytecode-builder bookkeeping failure that occurs while compiling SQL, before any execution.","triggerScenarios":"Codegen paths that hold a CursorID across conditional allocation branches (index lookup vs table scan) and then resolve the index for an id from the branch that was not taken; emitting index opcodes before their cursor is allocated; cursor renumbering/compaction dropping a slot that is still referenced.","commonSituations":"Forks or PRs that reorder emit_open_read calls in core/translate/; plans with many tables and secondary indexes where cursor ids shift; version upgrades changing cursor numbering.","solutions":["Report the panic with the SQL — cursor references must stay in lockstep with allocation; this is an internal bug","If you maintain a fork, audit the failing translate/ path for a CursorID captured before conditional cursor allocation (compare the index branch vs table branch of the plan)","Reduce index usage in the query (drop a secondary index or reorder joins) so the problematic allocation branch changes","Upgrade to a version with the allocation fix"],"exampleFix":"// before: assuming the id was allocated\nlet index = program_builder.resolve_index_for_cursor_id(cursor_id);\n\n// after: check the slot first and surface a translation error\nlet Some((_, CursorType::BTreeIndex(index))) = cursor_ref.get(cursor_id) else {\n    crate::bail_parse_error!(\"cursor {cursor_id} not allocated as an index cursor\");\n};\nindex.clone()","handlingStrategy":"type-guard","validationCode":"if builder.get_cursor_type(cursor_id).is_none() {\n    crate::bail_parse_error!(\"cursor {cursor_id} was never allocated\");\n}","typeGuard":"fn index_cursor(b: &ProgramBuilder, id: CursorID) -> Option<&Arc<Index>> {\n    match b.get_cursor_type(id) {\n        Some(CursorType::BTreeIndex(idx)) => Some(idx),\n        _ => None,\n    }\n}","tryCatchPattern":null,"preventionTips":["Never carry a CursorID across a conditional allocation branch without re-resolving it","Run a post-translation debug pass verifying every cursor reference exists in cursor_ref","Run cargo test plus the sqltest conformance runner after touching cursor allocation"],"tags":["vdbe","codegen","cursor","bounds","index","panic","prepare-time"],"backgroundTag":"cursor-id-out-of-bounds","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"}