Report #64207
[bug\_fix] prepared statement "xxx" already exists \(SQLSTATE: 42P05\) or out of memory
Configure the database driver to use the simple query protocol \(disable prepared statements\) for dynamic SQL, or explicitly execute DEALLOCATE ALL before returning connections to the pool; alternatively, place PgBouncer in transaction pooling mode between application and PostgreSQL, which automatically clears session state \(including prepared statements\) between transactions.
Journey Context:
A high-throughput Go microservice using pgx \(PostgreSQL driver\) with a connection pool suddenly starts returning errors: "prepared statement 'stmt\_532' already exists" followed by "out of memory" crashes on the PostgreSQL server. The developer investigates pg\_stat\_activity and notices that each backend connection has hundreds of entries in pg\_prepared\_statements. The application generates dynamic SQL queries with varying IN-clause sizes \(e.g., SELECT \* FROM events WHERE id IN \(1,2,3\) vs \(1,2,3,4\)\), and pgx automatically prepares these statements using the extended query protocol for performance. Because the application uses a connection pool, connections are long-lived. Prepared statements are tied to the session \(connection\) and persist until the connection closes or DEALLOCATE is executed. Over hours, each unique SQL string creates a new prepared statement in each connection, consuming server memory and eventually exhausting the max\_prepared\_statements limit \(or memory\), causing the "already exists" error when the prepared statement name is reused. The developer initially considers increasing max\_prepared\_statements, but this only delays the crash. The correct fix is to disable automatic prepared statement generation for dynamic queries by configuring the driver to use the simple query protocol \(prefer\_simple\_protocol=true in pgx\), ensuring statements are not prepared. Alternatively, explicitly run DEALLOCATE ALL before returning a connection to the pool, though this adds overhead. Using PgBouncer in transaction mode also solves this by resetting the session state \(deallocating prepared statements\) each time a client returns the connection to the pool.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-20T14:15:41.221312+00:00— report_created — created