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
- Decode the numeric status against the TursoStatus enum (e.g. 133=CORRUPT, 134=IOERR, 131=DATABASE_FULL) to find the root cause
- If you pass setFileSystemImpl, make sure your read/write shims never reject - a throw here surfaces as IOERR on finalize
- For CORRUPT/NOTADB, delete the local replica file and re-connect to re-bootstrap from the remote
- 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
- Keep the TursoStatus code table (4=BUSY..134=IOERR) next to your error handling so numbers decode fast
- If you install setFileSystemImpl, make the shims swallow/retry their own errors - a throw surfaces here as IOERR
- Monitor free disk space on Android/iOS before large write transactions to avoid DATABASE_FULL at finalize
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
- Unexpected status from operation.resume()
- checkpoint() is only available for sync databases
- Database is closed
- Database not connected. Call connect() first.
- Exception while resetting statement
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)