remix-run/remix · error · Error
Postgres database cannot + method + while transactions are
Error message
Postgres database cannot + method + while transactions are open
What it means
The Postgres driver refuses destructive/lifecycle operations (wipe, close per the callers) while any transactions are still open, to avoid corrupting in-flight work. #assertNoOpenTransactions throws this when the internal transactions map is non-empty.
Source
Thrown at packages/data-table-postgres/src/lib/driver.ts:446
// pg pools reject end() when called twice, so ending must be tracked to
// keep close() idempotent.
if (isPostgresPool(this.#client) && !this.#poolClosed) {
this.#poolClosed = true
await this.#client.end()
}
}
#configOrThrow(method: string): PostgresPoolConfig {
if (!this.#config) {
throw new Error('Postgres database ' + method + '() requires config-based construction')
}
return this.#config
}
#assertNoOpenTransactions(method: string): void {
if (this.#transactions.size > 0) {
throw new Error('Postgres database cannot ' + method + ' while transactions are open')
}
}
#maintenanceConfig(targetDatabase: string): PostgresClientConfig {
let maintenanceDatabase = this.#maintenanceDatabase
if (maintenanceDatabase === targetDatabase) {
maintenanceDatabase = targetDatabase === 'postgres' ? 'template1' : 'postgres'
}
let config = this.#configOrThrow('maintenance')
let connectionString = replaceDatabaseInConnectionString(
config?.connectionString,
maintenanceDatabase,
)
return { ...config, connectionString, database: maintenanceDatabase }
}View on GitHub (pinned to 9696913134)
Solutions
- Ensure every beginTransaction has a matching commit or rollback (try/finally) before wipe/close
- Use the managed transaction API that auto-commits/rolls back
- Await all in-flight request work before calling wipe() or close() in teardown
Example fix
// before
let token = await driver.beginTransaction()
// early return leaks the transaction
await driver.wipe() // throws
// after
let token = await driver.beginTransaction()
try { /* work */ await driver.commitTransaction(token) }
catch (e) { await driver.rollbackTransaction(token); throw e } Defensive patterns
Strategy: validation
Validate before calling
// ensure no open transactions before wipe/close
if ((driver as any).hasOpenTransactions?.()) { /* drain first */ } Try / catch
try { await driver.wipe() } catch (e) { if (e instanceof Error && e.message.includes('while transactions are open')) { await drainPendingWork(); await driver.wipe() } else throw e } Prevention
- Always pair beginTransaction with commit/rollback in try/finally
- Await in-flight requests before teardown
- Use the managed transaction API
When it happens
Trigger: Calling driver.wipe() or close() after beginTransaction without a matching commit/rollback; a leaked transaction from an early return or swallowed error; concurrent request still inside a transaction while test teardown calls wipe().
Common situations: Test teardown wiping the database while an async handler holds a transaction; error paths that begin a transaction but skip rollback; awaiting close() in a server shutdown hook with pending transactions.
Related errors
- Unknown transaction token: + token.id
- SQLite database cannot ' + method + ' while transactions are
- MySQL database cannot + method + while transactions are op
- Postgres database + method + () requires config-based const
- Unknown transaction token: ' + token.id
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/102a11c67e15286c.
Report an issue: GitHub.