tursodatabase/turso · error · Error
Statement step failed with status
Error message
Statement step failed with status: ${status} What it means
In get(), after stepWithIo() the status must be ROW (row available) or DONE (no rows); any other status — beyond the IO case already handled inside stepWithIo — throws with the numeric code. This wraps native step failures that are neither a row nor completion.
Solutions
- Log the status code and SQL; reproduce with db.exec/db.all to see if it is statement-specific or connection-wide
- Rebuild the native app so JS and native layers come from the same release of the package
- Recover by closing and reopening the Database (new connection state)
- If reproducible on current versions, file an issue with the minimal SQL and status code
Example fix
// before
const user = await db.get('SELECT * FROM users WHERE id = ?', [id]); // throws status error
// after: fail soft and reset the connection once
let user;
try {
user = await db.get('SELECT * FROM users WHERE id = ?', [id]);
} catch (e) {
await resetDatabaseConnection(); // close, new Database, connect
user = await db.get('SELECT * FROM users WHERE id = ?', [id]);
} Defensive patterns
Strategy: retry
Validate before calling
// Cheap connection probe before reads that matter
await db.get('SELECT 1');
const user = await db.get('SELECT * FROM users WHERE id = ?', [id]); Try / catch
try {
return await db.get(sql, params);
} catch (e) {
if (e instanceof Error && /step failed with status/.test(e.message)) {
await resetDatabaseConnection(); // close + new Database + connect
return db.get(sql, params); // single retry on clean state
}
throw e;
} Prevention
- Rebuild native and JS together on upgrades; version skew produces these status escapes
- Always capture the status code and SQL in error reporting for native triage
- Don't loop retries on this error — one recovery attempt, then surface
When it happens
Trigger: A SELECT whose native step returns an unexpected status code (native error/invalid state), typically after earlier errors invalidated the statement or connection, or from a version mismatch between the JS package and the compiled native library.
Common situations: Rare; appears alongside native-layer faults. Most often when the app was partially upgraded (new JS, old native binary) or after a prior error on the same connection left it in a bad state. The status number in the message is the key diagnostic.
Related errors
- getAllRows failed with status
- Statement execution failed with status
- Statement has been finalized
- Unknown parameter name
- Unsupported parameter type
AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20).
Data as JSON: /api/errors/72be116201fd7faf.
Report an issue: GitHub.
Appendix: source
Thrown at bindings/react-native/src/Statement.ts:264
try {
// Bind parameters inside the lock to prevent concurrent bind/execute races
if (params.length > 0) {
this.bind(...params);
}
// Step once with async IO handling
const status = await this.stepWithIo();
if (status === TursoStatus.ROW) {
const row = this.readRow();
return row;
}
if (status === TursoStatus.DONE) {
return undefined;
}
throw new Error(`Statement step failed with status: ${status}`);
} finally {
this._statement.reset();
if (this._execLock) {
this._execLock.release();
}
}
}
/**
* Execute statement and return all rows
*
* @param params - Optional parameters to bind
* @returns Array of rows
*/
async all(...params: BindParams[]): Promise<Row[]> {
if (this._finalized) {
throw new Error('Statement has been finalized');
}View on GitHub (pinned to 244cde92a7)