tursodatabase/turso · error · Error
Statement step failed with status: ${status}
Error message
Statement step failed with status: ${status} What it means
Statement.get() steps the statement once via stepWithIo(); TursoStatus.ROW (2) yields the row and DONE (1) yields undefined. Any other status falls through to this throw with the numeric code, which decodes against the TursoStatus enum: 4=BUSY, 5=INTERRUPT, 127=ERROR, 128=MISUSE, 133=CORRUPT, 134=IOERR. Unlike rawRun's executor path, this is a single-step read path, so failure statuses here usually mean the step itself could not be performed.
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 bad083fafb)
Solutions
- Decode the numeric status against the TursoStatus enum to identify the class of failure
- Status 4 (BUSY): retry get() after a short backoff, or schedule reads outside write windows
- Status 128 (MISUSE): ensure the statement was reset/rebound correctly between executions
- Status 127/133/134: verify database file integrity and that the sync engine finished applying changes
Example fix
// before
try {
const row = await stmt.get(id);
} catch (e) { /* opaque failure */ }
// after
import { TursoStatus } from '@tursodatabase/sync-react-native';
try {
const row = await stmt.get(id);
} catch (e) {
const m = /status: (\d+)$/.exec(String(e.message));
if (m && Number(m[1]) === TursoStatus.BUSY) {
await new Promise(r => setTimeout(r, 50));
return await stmt.get(id); // one retry
}
throw e;
} Defensive patterns
Strategy: try-catch
Type guard
function isTursoStatusError(e: unknown): boolean {
return e instanceof Error && /status: \d+$/.test(e.message);
} Try / catch
try {
const row = await stmt.get(id);
} catch (e) {
const m = /status: (\d+)$/.exec(String((e as Error).message));
const code = m ? Number(m[1]) : null;
if (code === TursoStatus.BUSY) { /* backoff and retry once */ }
else if (code === TursoStatus.MISUSE) { /* reset + rebind, then retry */ }
else throw e;
} Prevention
- Decode numeric statuses against TursoStatus before deciding retry vs. fail
- Retry BUSY (4) with a bounded backoff instead of failing the read
- Ensure statements are reset between executions so steps never start in a bad state
When it happens
Trigger: A SELECT via stmt.get() hitting a BUSY (4) database when another connection holds the write lock; a step interrupted mid-flight (5); MISUSE (128) from stepping a statement in a bad state after abnormal bind/reset sequences; engine-level ERROR (127) from a corrupt or unreadable page.
Common situations: Concurrent read-during-write contention in multi-connection setups; background sync writing while the UI thread calls get(); a database file corrupted after an unclean shutdown.
Related errors
- Statement execution failed with status: ${result.status}
- getAllRows failed with status: ${bulk.status}
- Statement finalization failed with status: ${status}
- push() is only available for sync databases
- pull() is only available for sync databases
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/72be116201fd7faf.
Report an issue: GitHub.