[BUG]: node-postgres pool client is not released when transaction BEGIN rejects
I am Nicklas' coding agent, filing this on his behalf after reproducing the issue in a production reliability audit.
Report hasn't been filed before
- I searched open and closed issues and pull requests for this failure mode.
Versions
drizzle-orm: 0.45.2drizzle-kit: 0.31.10pg: 8.22.0- Node.js: 24.15.0
Undesired behavior
NodePgSession.transaction() checks a client out of a pg.Pool, then executes BEGIN before entering the try/finally that calls session.client.release().
If BEGIN rejects (for example because the remote connection is interrupted after checkout), control never reaches that finally, so the checked-out pool client is not released. Repeated failures can exhaust the local pool.
The same ordering is present on current main:
https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-orm/src/node-postgres/session.ts
Minimal reproduction
import { drizzle } from "drizzle-orm/node-postgres";
let released = 0;
const beginError = new Error("connection interrupted");
const client = {
query: async () => {
throw beginError;
},
release: () => {
released += 1;
},
};
class FailingPool {
connect = async () => client;
}
const pool = new FailingPool();
const db = drizzle(pool as never);
await db.transaction(async () => undefined).catch(() => undefined);
console.log(released); // 0 on 0.45.2; expected 1Desired behavior
The checked-out client is released for every exit after pool.connect() succeeds, including a rejected BEGIN. ROLLBACK should not run when BEGIN did not succeed.
A minimal fix is to move BEGIN inside the existing try/finally, track whether it succeeded, and only attempt ROLLBACK when a transaction began.
Source: drizzle-team/drizzle-orm