tursodatabase/turso · critical
Function should only be called after `SQLITE_ROW`
Error message
Function should only be called after `SQLITE_ROW`
What it means
In the C compatibility layer, sqlite3_column_type calls stmt.row().expect("Function should only be called after SQLITE_ROW"). If no row is current - never stepped, stepped to SQLITE_DONE, or after reset - the expect panics, and a panic across the C FFI boundary aborts the process. This is stricter than upstream sqlite3, which tolerates column access after DONE.
Source
Thrown at bindings/c/src/lib.rs:2327
return SQLITE_MISUSE;
}
let stmt_ref = &mut *stmt;
stmt_ref.stmt.clear_bindings();
SQLITE_OK
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_column_type(
stmt: *mut sqlite3_stmt,
idx: ffi::c_int,
) -> ffi::c_int {
let stmt = &mut *stmt;
let row = stmt
.stmt
.row()
.expect("Function should only be called after `SQLITE_ROW`");
match row.get::<&Value>(idx as usize) {
Ok(turso_core::Value::Numeric(turso_core::Numeric::Integer(_))) => SQLITE_INTEGER,
Ok(turso_core::Value::Text(_)) => SQLITE_TEXT,
Ok(turso_core::Value::Numeric(turso_core::Numeric::Float(_))) => SQLITE_FLOAT,
Ok(turso_core::Value::Blob(_)) => SQLITE_BLOB,
_ => SQLITE_NULL,
}
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_column_count(stmt: *mut sqlite3_stmt) -> ffi::c_int {
let stmt = &mut *stmt;
stmt.stmt.num_columns() as ffi::c_int
}
#[no_mangle]
pub unsafe extern "C" fn sqlite3_column_decltype(View on GitHub (pinned to 492c4a71cd)
Solutions
- Gate every column access on `sqlite3_step(stmt) == SQLITE_ROW`
- Check every sqlite3_step return code (ROW, DONE, BUSY, ERROR) before touching columns
- After sqlite3_reset, always step again before any column call
- Refactor code that relies on sqlite3's post-DONE leniency - this binding deliberately aborts instead
Example fix
// before
sqlite3_step(stmt);
int t = sqlite3_column_type(stmt, 0); // aborts when step returned SQLITE_DONE
// after
if (sqlite3_step(stmt) == SQLITE_ROW) {
int t = sqlite3_column_type(stmt, 0);
} Defensive patterns
Strategy: validation
Validate before calling
int rc = sqlite3_step(stmt);
if (rc == SQLITE_ROW) {
int t = sqlite3_column_type(stmt, 0);
/* safe */
} Prevention
- Never call sqlite3_column_* without a fresh SQLITE_ROW
- Check every sqlite3_step return code (ROW, DONE, BUSY, ERROR)
- Follow the order reset, step, then column access - every time
When it happens
Trigger: Calling sqlite3_column_type (or sibling column accessors routed the same way) before the first successful sqlite3_step, after a step returned SQLITE_DONE/ERROR/BUSY, or after sqlite3_reset - i.e. without a fresh SQLITE_ROW.
Common situations: Ported C code that forgets to check the step return code, do-while loop structures that call column functions unconditionally, or code relying on sqlite3's lenient post-DONE behavior.
Related errors
- buffer not loaded
- OpenRead on pseudo cursor
- Rewind on non-btree/materialized-view cursor
- unexpected cursor type
- Next on non-btree/materialized-view cursor
AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-08-20).
Data as JSON: /api/errors/d8b48183b4f23f6c.
Report an issue: GitHub.