tursodatabase/turso · error · Error

Statement finalization failed with status

Error message

Statement finalization failed with status: ${status}

What it means

Statement.finalize() loops the native finalize() call, handling IO (partial-sync page loads) along the way, and expects DONE. Any other TursoStatus - BUSY(4), INTERRUPT(5), BUSY_SNAPSHOT(6), ERROR(127), MISUSE(128), CONSTRAINT(129), READONLY(130), DATABASE_FULL(131), NOTADB(132), CORRUPT(133), IOERR(134) - aborts with this error. The number in the message is the engine's raw status code for why the statement could not be finalized cleanly.

Solutions

  1. Decode the numeric status against the TursoStatus enum (e.g. 133=CORRUPT, 134=IOERR, 131=DATABASE_FULL) to find the root cause
  2. If you pass setFileSystemImpl, make sure your read/write shims never reject - a throw here surfaces as IOERR on finalize
  3. For CORRUPT/NOTADB, delete the local replica file and re-connect to re-bootstrap from the remote
  4. Free device storage if DATABASE_FULL; otherwise close and recreate the Database and prepare fresh statements

Example fix

// before
await stmt.finalize(); // throws 'Statement finalization failed with status: 133'

// after - decode status and recover the replica
try {
  await stmt.finalize();
} catch (e) {
  const status = Number(e.message.match(/status: (\d+)/)?.[1]);
  if (status === 133 /* CORRUPT */ || status === 132 /* NOTADB */) {
    await db.close();
    await RNFS.unlink(dbPath); // drop damaged replica
    db = await connect(opts);  // re-bootstrap from remote
  } else {
    throw e;
  }
}
Defensive patterns

Strategy: try-catch

Try / catch

try { await stmt.finalize(); } catch (e) { const m = e.message.match(/status: (\d+)/); const status = m ? Number(m[1]) : null; // 133=CORRUPT,132=NOTADB -> drop replica and re-bootstrap; 131 -> free storage; else rethrow }

Prevention

When it happens

Trigger: await stmt.finalize() while the underlying database is in a bad state: CORRUPT/NOTADB from a damaged local file, IOERR from a failing file layer (custom setFileSystemImpl that throws), MISUSE from double-finalization at the native level, or DATABASE_FULL on a full disk.

Common situations: App killed mid-write leaving a corrupt local replica; a custom file-system implementation (encryption/compression shim) that raises during the final flush; device storage exhausted; finalizing inside error-recovery code that runs while the connection is already broken.

Related errors


AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20). Data as JSON: /api/errors/f7e766ae870b3755. Report an issue: GitHub.

Appendix: source

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

    }
    try {
      while (true) {
        const status = this._statement.finalize();

        if (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 (status !== TursoStatus.DONE) {
          throw new Error(`Statement finalization failed with status: ${status}`);
        }
        break;
      }
      this._finalized = true;
    } finally {
      if (this._execLock) {
        this._execLock.release();
      }
    }
  }

  /**
   * Check if statement has been finalized
   */
  get finalized(): boolean {
    return this._finalized;
  }
}

View on GitHub (pinned to 244cde92a7)