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

  1. Log the status code and SQL; reproduce with db.exec/db.all to see if it is statement-specific or connection-wide
  2. Rebuild the native app so JS and native layers come from the same release of the package
  3. Recover by closing and reopening the Database (new connection state)
  4. 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

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


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)