tursodatabase/turso · error · Error

Statement execution failed with status: ${result.status}

Error message

Statement execution failed with status: ${result.status}

What it means

Inside rawRun()'s executeWithIo loop, TursoStatus.IO (3) is handled by draining sync-engine IO, and any status other than TursoStatus.DONE (1) is rejected with its numeric code. The number maps to the TursoStatus enum in types.ts: 4=BUSY, 5=INTERRUPT, 127=ERROR, 128=MISUSE, 129=CONSTRAINT, 130=READONLY, 131=DATABASE_FULL, 132=NOTADB, 133=CORRUPT, 134=IOERR. The message is a raw number, so decoding it against the enum is the first diagnostic step.

Source

Thrown at bindings/react-native/src/Statement.ts:199

   */
  private async executeWithIo(): Promise<{ status: number; rowsChanged: number }> {
    while (true) {
      const result = this._statement.execute();

      if (result.status === TursoStatus.IO) {
        // Statement needs IO (e.g., loading missing pages with partial sync)
        this._statement.runIo();

        // Drain sync engine IO queue
        if (this._extraIo) {
          await this._extraIo();
        }

        continue;
      }

      if (result.status !== TursoStatus.DONE) {
        throw new Error(`Statement execution failed with status: ${result.status}`);
      }

      return result;
    }
  }

  /**
   * Step statement once handling potential IO (for partial sync)
   * Matches Python's _step_once_with_io pattern
   *
   * @returns Status code
   */
  private async stepWithIo(): Promise<number> {
    while (true) {
      const status = this._statement.step();

      if (status === TursoStatus.IO) {
        // Statement needs IO (e.g., loading missing pages with partial sync)

View on GitHub (pinned to bad083fafb)

Solutions

  1. Map the numeric status to the TursoStatus enum from '@tursodatabase/sync-react-native' to identify the real failure
  2. Status 129 (CONSTRAINT): fix the data or add an ON CONFLICT clause / upsert to the SQL
  3. Status 4 (BUSY): serialize writers or retry the operation after a short backoff
  4. Status 130/131 (READONLY/DATABASE_FULL): check how the database was opened and free device storage
  5. Status 128 (MISUSE): verify the number of bound parameters matches the statement's placeholders

Example fix

// before
await db.exec(stmt, [duplicateId]); // throws: Statement execution failed with status: 129

// after
const fresh = db.prepare('INSERT INTO t(id) VALUES (?) ON CONFLICT(id) DO UPDATE SET id=excluded.id');
await db.exec(fresh, [duplicateId]);
Defensive patterns

Strategy: try-catch

Validate before calling

import { TursoStatus } from '@tursodatabase/sync-react-native';

function describeStatus(e: unknown): string | null {
  const m = /status: (\d+)$/.exec(e instanceof Error ? e.message : '');
  return m ? TursoStatus[Number(m[1])] ?? `UNKNOWN(${m[1]})` : null;
}

Type guard

function isStatusError(e: unknown, status: TursoStatus): boolean {
  return e instanceof Error && e.message.endsWith(`status: ${status}`);
}

Try / catch

try {
  await db.exec(stmt, params);
} catch (e) {
  if (isStatusError(e, TursoStatus.BUSY)) {
    await new Promise(r => setTimeout(r, 50));
    return db.exec(stmt, params); // bounded retry
  }
  if (isStatusError(e, TursoStatus.CONSTRAINT)) {
    // duplicate key etc. — surface a domain error, not a crash
    throw new DuplicateKeyError(primaryKey);
  }
  throw e;
}

Prevention

When it happens

Trigger: INSERT/UPDATE/DELETE through db.exec() or a Transaction that violates UNIQUE, NOT NULL, or a foreign key (status 129); writing to a read-only database (130); device storage exhausted mid-transaction (131); another connection holding the write lock (4); binding mismatches or reusing a misused statement (128).

Common situations: Duplicate-key inserts inside Transaction.run(); migrating code from better-sqlite3/libsql and expecting string error codes like 'SQLITE_CONSTRAINT' but getting 129; MVCC/multi-connection contention surfacing as 4; large sync writes filling device storage.

Related errors


AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16). Data as JSON: /api/errors/9bf65606e9124363. Report an issue: GitHub.