{"record":{"id":"102a11c67e15286c","repo":"remix-run/remix","slug":"postgres-database-cannot-method-while-transa","errorCode":null,"errorMessage":"Postgres database cannot  + method +  while transactions are open","messagePattern":"Postgres database cannot  \\+ method \\+  while transactions are open","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/data-table-postgres/src/lib/driver.ts","lineNumber":446,"sourceCode":"    // pg pools reject end() when called twice, so ending must be tracked to\n    // keep close() idempotent.\n    if (isPostgresPool(this.#client) && !this.#poolClosed) {\n      this.#poolClosed = true\n      await this.#client.end()\n    }\n  }\n\n  #configOrThrow(method: string): PostgresPoolConfig {\n    if (!this.#config) {\n      throw new Error('Postgres database ' + method + '() requires config-based construction')\n    }\n\n    return this.#config\n  }\n\n  #assertNoOpenTransactions(method: string): void {\n    if (this.#transactions.size > 0) {\n      throw new Error('Postgres database cannot ' + method + ' while transactions are open')\n    }\n  }\n\n  #maintenanceConfig(targetDatabase: string): PostgresClientConfig {\n    let maintenanceDatabase = this.#maintenanceDatabase\n\n    if (maintenanceDatabase === targetDatabase) {\n      maintenanceDatabase = targetDatabase === 'postgres' ? 'template1' : 'postgres'\n    }\n\n    let config = this.#configOrThrow('maintenance')\n    let connectionString = replaceDatabaseInConnectionString(\n      config?.connectionString,\n      maintenanceDatabase,\n    )\n\n    return { ...config, connectionString, database: maintenanceDatabase }\n  }","sourceCodeStart":428,"sourceCodeEnd":464,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/data-table-postgres/src/lib/driver.ts#L428-L464","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","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"],"exampleFix":"// before\nlet token = await driver.beginTransaction()\n// early return leaks the transaction\nawait driver.wipe() // throws\n\n// after\nlet token = await driver.beginTransaction()\ntry { /* work */ await driver.commitTransaction(token) }\ncatch (e) { await driver.rollbackTransaction(token); throw e }","handlingStrategy":"validation","validationCode":"// ensure no open transactions before wipe/close\nif ((driver as any).hasOpenTransactions?.()) { /* drain first */ }","typeGuard":null,"tryCatchPattern":"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 }","preventionTips":["Always pair beginTransaction with commit/rollback in try/finally","Await in-flight requests before teardown","Use the managed transaction API"],"tags":["postgres","transaction","lifecycle","driver"],"backgroundTag":"open-transactions-block-operation","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}