tursodatabase/turso · error · Error
Statement finalization failed with status: ${status}
Error message
Statement finalization failed with status: ${status} What it means
Statement.finalize() loops the native finalize call, servicing TursoStatus.IO by running runIo() and draining _extraIo; a non-DONE status throws with its numeric code (decode via TursoStatus: 4=BUSY, 127=ERROR, 128=MISUSE). Because this._finalized = true is only assigned after the loop breaks successfully, a throw here leaves the statement NOT marked finalized — finalize can be retried.
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 bad083fafb)
Solutions
- Decode the numeric status via the TursoStatus enum to identify the cause
- Await outstanding statements/operations before closing, so finalize runs uncontended
- Status 4 (BUSY): wait briefly and retry finalize() — it is safe to retry since _finalized was not set
- Status 127/IO-related: verify connectivity during teardown or accept an unclean close and let recovery handle it
Example fix
// before
await stmt.finalize(); // throws during teardown: Statement finalization failed with status: 4
// after
async function finalizeRetry(stmt, tries = 3) {
for (let i = 0; ; i++) {
try { return await stmt.finalize(); }
catch (e) {
if (i < tries - 1 && /status: 4$/.test(e.message)) {
await new Promise(r => setTimeout(r, 50));
continue;
}
throw e;
}
}
} Defensive patterns
Strategy: retry
Type guard
function isFinalizeStatusError(e: unknown): boolean {
return e instanceof Error && /Statement finalization failed with status: \d+/.test(e.message);
} Try / catch
async function finalizeSafely(stmt: Statement, tries = 3): Promise<void> {
for (let i = 0; ; i++) {
try { return await stmt.finalize(); }
catch (e) {
const busy = e instanceof Error && e.message.endsWith(`status: ${TursoStatus.BUSY}`);
if (i < tries - 1 && busy) {
await new Promise(r => setTimeout(r, 50 * (i + 1)));
continue; // safe: _finalized was not set on throw
}
throw e;
}
}
} Prevention
- Quiesce outstanding operations before closing statements or the database
- Remember finalize() is retryable when it throws — the flag is only set on success
- Decode the status: BUSY(4) is retryable, ERROR(127) needs investigation
When it happens
Trigger: Finalizing while the statement/database is BUSY (4) from another connection; pending sync IO that cannot complete during teardown (network gone while finalize needs to flush); native ERROR (127) during resource release.
Common situations: Closing a Database on app background while background sync still writes; finalizing inside an offline state where the IO drain cannot reach the sync server; teardown races at app exit.
Related errors
- Statement execution failed with status: ${result.status}
- Statement step failed with status: ${status}
- getAllRows failed with status: ${bulk.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/f7e766ae870b3755.
Report an issue: GitHub.