{"record":{"id":"4e972e379c8e391f","repo":"tursodatabase/turso","slug":"cursor-not-found-key","errorCode":null,"errorMessage":"Cursor not found: {key:?}","messagePattern":"Cursor not found: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/vdbe/builder.rs","lineNumber":1857,"sourceCode":"    // translate [CursorKey] to cursor id\n    pub fn resolve_cursor_id_safe(&self, key: &CursorKey) -> Option<CursorID> {\n        // Check cursor overrides first, only apply override for table cursors.\n        // Index cursor lookups are not overridden because when a cursor override is active,\n        // the calling code (translate_expr) should skip index logic entirely.\n        if key.index.is_none() && !key.is_build {\n            let table_id: usize = key.table_reference_id.into();\n            if let Some(&cursor_id) = self.cursor_overrides.get(&table_id) {\n                return Some(cursor_id);\n            }\n        }\n        self.cursor_ref\n            .iter()\n            .position(|(k, _)| k.as_ref().is_some_and(|k| k.equals(key)))\n    }\n\n    pub fn resolve_cursor_id(&self, key: &CursorKey) -> CursorID {\n        self.resolve_cursor_id_safe(key)\n            .unwrap_or_else(|| panic!(\"Cursor not found: {key:?}\"))\n    }\n\n    /// Resolve the first allocated index cursor for a given table reference.\n    /// This method exists due to a limitation of our translation system where\n    /// a subquery that references an outer query table cannot know whether a\n    /// table cursor, index cursor, or both were opened for that table reference.\n    /// Hence: currently we first try to resolve a table cursor, and if that fails,\n    /// we resolve an index cursor via this method.\n    pub fn resolve_any_index_cursor_id_for_table(&self, table_ref_id: TableInternalId) -> CursorID {\n        self.resolve_any_index_cursor_id_for_table_safe(table_ref_id)\n            .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, _)| {","sourceCodeStart":1839,"sourceCodeEnd":1875,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/core/vdbe/builder.rs#L1839-L1875","documentation":"During bytecode building, resolve_cursor_id maps a CursorKey (table/index reference) to the CursorID of an earlier OpenRead/OpenIndex. If the key was never opened, it panics - the program references a cursor that translation never allocated. A sibling helper, resolve_cursor_id_safe, returns Option for cases where absence is legal.","triggerScenarios":"Internal: new planner or translation shapes that use a table/index cursor before (or without) emitting the corresponding open instruction; subqueries referencing outer-query cursors resolved against the wrong program; cursor_overrides wiring regressions.","commonSituations":"Contributor PRs to the optimizer/translator; correlated-subquery changes; refactors of cursor registration order.","solutions":["Emit/register the cursor (OpenRead/OpenIndex or cursor_overrides) before resolving its ID","Use resolve_cursor_id_safe where absence is expected and handle the None branch","Add an EXPLAIN-based regression test for the plan shape"],"exampleFix":"// before\nlet id = builder.resolve_cursor_id(&key); // panics: cursor never opened\n\n// after\nbuilder.open_table_cursor(key.clone());   // emit OpenRead first\nlet id = builder.resolve_cursor_id(&key);","handlingStrategy":"validation","validationCode":"// prefer the safe variant when the cursor may legitimately be absent\nmatch builder.resolve_cursor_id_safe(&key) {\n    Some(id) => id,\n    None => {\n        // emit OpenRead/OpenIndex for `key` first, then resolve again\n        builder.open_table_cursor(key.clone());\n        builder.resolve_cursor_id(&key)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Open then resolve: keep cursor registration adjacent to first use","Diff EXPLAIN output in tests whenever touching the planner or translator"],"tags":["vdbe","translation","cursor","compiler","internal","panic"],"backgroundTag":"unresolved-cursor-reference","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"}