remix-run/remix · error · Error
SQLite database cannot ' + method + ' while transactions are
Error message
SQLite database cannot ' + method + ' while transactions are open
What it means
The SQLite driver blocks lifecycle operations (wipe, close) while transactions remain open, throwing from #assertNoOpenTransactions when the transactions map is non-empty — same protection as the Postgres driver, preventing resets that would corrupt in-flight work.
Source
Thrown at packages/data-table-sqlite/src/lib/driver.ts:457
this.#replaceDatabase()
} else {
// The supplied client remains caller-owned, but this wrapper cannot safely
// reuse a connection whose transaction state is unknown.
this.#databaseOpen = false
}
}
#configOrThrow(method: string): SqliteDatabaseConfig {
if (!this.#config) {
throw new Error('SQLite database ' + method + '() requires config-based construction')
}
return this.#config
}
#assertNoOpenTransactions(method: string): void {
if (this.#transactions.size > 0) {
throw new Error('SQLite database cannot ' + method + ' while transactions are open')
}
}
#assertTransaction(token: TransactionToken): void {
this.#assertDatabaseOpen()
if (!this.#transactions.has(token.id)) {
throw new Error('Unknown transaction token: ' + token.id)
}
}
#assertDatabaseOpen(): void {
if (!this.#databaseOpen) {
throw new Error('SQLite database is closed')
}
}
}
const REMOVE_RETRIES = 10View on GitHub (pinned to 9696913134)
Solutions
- Guarantee commit/rollback in try/finally around every transaction, or use the managed transaction API
- Await all in-flight work before wipe()/close()
- In tests, use per-test drivers or ensure afterAll ordering runs after transactions settle
Example fix
// before
let tx = await db.beginTransaction()
// leaked on early return
await db.wipe() // throws
// after
let tx = await db.beginTransaction()
try { await work(tx); await db.commitTransaction(tx) }
catch (e) { await db.rollbackTransaction(tx); throw e } Defensive patterns
Strategy: validation
Validate before calling
// drain before teardown await Promise.allSettled(pendingTransactionWork) await db.wipe()
Try / catch
try { await db.close() } catch (e) { if (e instanceof Error && e.message.includes('while transactions are open')) { /* settle or force-rollback open transactions, then retry */ } throw e } Prevention
- try/finally around every transaction
- Await in-flight work in beforeAll/beforeExit hooks
- Use managed transactions everywhere
When it happens
Trigger: Calling wipe() or close() after a beginTransaction without a matching commit/rollback; leaked tokens from early returns; a still-running async operation holding a transaction during test teardown.
Common situations: Test teardown wiping SQLite while queries are pending; error paths that begin but never roll back; close() in a process shutdown hook racing in-flight transactions.
Related errors
- Postgres database cannot + method + while transactions are
- Unknown transaction token: ' + token.id
- MySQL database cannot + method + while transactions are op
- Unknown transaction token: + token.id
- SQLite config-based construction requires node:sqlite (Node.
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/4d23ebbd72cebb45.
Report an issue: GitHub.