Report #40934
[bug\_fix] prepared statement "S\_1" does not exist
Disable server-side prepared statements in the database driver configuration \(e.g., JDBC: \`prepareThreshold=0\`; Go/pgx: \`PreferSimpleProtocol: true\`; Node/pg: set \`prepare: false\` on queries\); alternatively, reconfigure PgBouncer to use session pooling mode \(losing multiplexing efficiency\) or use PgBouncer with \`max\_prepared\_statements\` setting if using protocol-level named prepared statements with recent PgBouncer versions.
Journey Context:
A Java Spring Boot service is migrated from direct PostgreSQL connections to PgBouncer \(transaction pooling mode\) to handle connection limits. Immediately upon deployment, the application throws \`PSQLException: ERROR: prepared statement "S\_1" does not exist\` on almost every query. The developer investigates the JDBC driver documentation and discovers that by default, the driver uses the extended query protocol \(server-side prepared statements\) after a statement is executed 5 times \(default \`prepareThreshold\`\). In transaction pooling mode, PgBouncer assigns each transaction to potentially different backend Postgres processes. When the client prepares a statement on Backend A, then sends the execute command on a new transaction that lands on Backend B, the statement handle "S\_1" does not exist in that backend's memory. The root cause is the impedance mismatch between connection-level state \(prepared statements\) and PgBouncer's connection reuse strategy. The fix works by forcing the driver to use the simple query protocol \(text-based, no server-side prepared statements\), making each query self-contained and stateless with respect to the connection, thus compatible with PgBouncer's multiplexing.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T23:10:45.227234+00:00— report_created — created