tursodatabase/turso · error
No index cursor found for table {table_ref_id}
Error message
No index cursor found for table {table_ref_id} What it means
Raised in the SQL-to-bytecode builder when resolve_any_index_cursor_id_for_table() finds no index cursor allocated for a table reference. The method exists because a correlated subquery that references an outer query table cannot know whether the outer plan opened a table cursor, an index cursor, or both, so translation resolves whichever exists. The panic means translation demanded an index cursor the planner never allocated — an internal codegen/plan inconsistency, not bad user data.
Source
Thrown at core/vdbe/builder.rs:1868
self.cursor_ref
.iter()
.position(|(k, _)| k.as_ref().is_some_and(|k| k.equals(key)))
}
pub fn resolve_cursor_id(&self, key: &CursorKey) -> CursorID {
self.resolve_cursor_id_safe(key)
.unwrap_or_else(|| panic!("Cursor not found: {key:?}"))
}
/// Resolve the first allocated index cursor for a given table reference.
/// This method exists due to a limitation of our translation system where
/// a subquery that references an outer query table cannot know whether a
/// table cursor, index cursor, or both were opened for that table reference.
/// Hence: currently we first try to resolve a table cursor, and if that fails,
/// we resolve an index cursor via this method.
pub fn resolve_any_index_cursor_id_for_table(&self, table_ref_id: TableInternalId) -> CursorID {
self.resolve_any_index_cursor_id_for_table_safe(table_ref_id)
.unwrap_or_else(|| panic!("No index cursor found for table {table_ref_id}"))
}
pub fn resolve_any_index_cursor_id_for_table_safe(
&self,
table_ref_id: TableInternalId,
) -> Option<CursorID> {
self.cursor_ref.iter().position(|(k, _)| {
k.as_ref()
.is_some_and(|k| k.table_reference_id == table_ref_id && k.index.is_some())
})
}
/// Resolve the [Index] that a given cursor is associated with.
pub fn resolve_index_for_cursor_id(&self, cursor_id: CursorID) -> Arc<Index> {
let cursor_ref = &self
.cursor_ref
.get(cursor_id)
.unwrap_or_else(|| panic!("Cursor not found: {cursor_id}"))View on GitHub (pinned to 492c4a71cd)
Solutions
- File a bug with the exact SQL — the resolver must not assume an index cursor exists; prepare-time reproduction makes it cheap to fix
- Rewrite the correlated subquery as a JOIN or grouped derived table so no outer-reference cursor resolution is needed
- Change the plan: drop/disable the relevant index or use +col / NOT INDEXED so the index-cursor code path is not taken
- Upgrade to a release where subquery cursor resolution is fixed
Example fix
-- before: correlated subquery resolves an outer index cursor SELECT a, (SELECT SUM(b) FROM t2 WHERE t2.k = t1.indexed_k) FROM t1; -- after: join form avoids outer-reference cursor resolution SELECT t1.a, agg.s FROM t1 LEFT JOIN (SELECT k, SUM(b) AS s FROM t2 GROUP BY k) AS agg ON agg.k = t1.indexed_k;
Defensive patterns
Strategy: try-catch
Try / catch
// Treat statement preparation as fallible: a builder panic is a codegen bug,
// not something to unwind through your embedding layer.
let prepared = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
conn.prepare(sql)
}))
.map_err(|_| ApiError::QueryUnsupported { sql: sql.to_string() })??; Prevention
- Prepare every statement at app startup so codegen panics surface in CI, not production
- Keep a differential harness comparing Turso vs sqlite3 output (scripts/diff.sh) over your query corpus
- Pin the engine version and smoke-test planner-heavy queries (correlated subqueries, EXISTS, IN) after upgrades
When it happens
Trigger: Preparing a query with a correlated subquery (SELECT-list scalar subquery, EXISTS, IN) where the outer table reference has only a table cursor (full scan chosen, NOT INDEXED, or no matching index) while the subquery translation path unconditionally resolves an index cursor via resolve_any_index_cursor_id_for_table. Surfaces at statement preparation time, often after optimizer or subquery-decorrelation changes.
Common situations: Version upgrades that changed cursor allocation for correlated references; queries mixing indexed and non-indexed outer columns; development branches touching core/translate/ subquery flattening or co-routines; reproduces deterministically for a given SQL string.
Related errors
- Cursor not found: {cursor_id}
- Cursor is not an index: {cursor_id}
- Cursor not found: {key:?}
- cursor id {cursor_id} out of bounds
- cursor id {cursor_id} is None
AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-08-20).
Data as JSON: /api/errors/aad9e9d4fecc9778.
Report an issue: GitHub.