Report #14291
[bug\_fix] prepared statement does not exist / bind message supplies X parameters - PgBouncer protocol conflict
Disable server-side prepared statements in the driver \(e.g., set prepareThreshold=0 in JDBC, or use unnamed prepared statements\), or switch PgBouncer to session pooling mode instead of transaction pooling.
Journey Context:
You migrate your Java Spring Boot app to use PgBouncer in transaction pooling mode to handle connection limits. Suddenly, queries fail with 'prepared statement "S\_1" does not exist'. You check the Postgres logs and see the prepare occurring on one backend, but the execute happening on another. You realize that in transaction pooling mode, PgBouncer switches the client between different Postgres backends for each transaction. Protocol-level prepared statements \(the default in JDBC's prepareThreshold=5\) are tied to a specific backend process. When the client is reassigned to a new backend, the prepared statement handle is invalid. You initially try increasing prepared statement cache in the app, which makes it worse. The fix is either configuring the JDBC driver with 'prepareThreshold=0' to use client-side parameterized queries \(protocol-level unnamed prepared statements\), or switching PgBouncer to session pooling mode, which keeps the client pinned to one backend for the duration of the connection, preserving prepared statement state.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T21:12:50.761349+00:00— report_created — created