Report #11207
[bug\_fix] Prepared statement does not exist \(with PgBouncer\)
Disable server-side prepared statements in the client driver \(e.g., \`prepareThreshold=0\` for libpq/JDBC, or \`?preparedStatements=false\` in connection strings\) or switch PgBouncer to \`pool\_mode=session\`. Root cause: PgBouncer in transaction mode multiplexes client connections onto fewer Postgres backends; prepared statements are session-scoped and do not survive connection reassignment.
Journey Context:
An engineer migrates a Java Spring Boot application to use PgBouncer to handle connection limits. They configure PgBouncer with \`pool\_mode = transaction\` for maximum efficiency. Immediately, the app starts throwing errors: 'prepared statement S\_1 does not exist' and 'bind message supplies 0 parameters, but prepared statement requires 2'. The developer checks the Postgres logs and sees the bind is failing. They realize that JDBC by default uses server-side prepared statements \(via PREPARE\) after a threshold \(usually 5 executions\). Under PgBouncer transaction mode, each transaction might run on a different physical Postgres backend. The PREPARE was done on Backend A, but the EXECUTE is sent to Backend B via PgBouncer, which doesn't have that prepared statement handle. The solution is to disable server-side prepared statements in the JDBC URL \(\`prepareThreshold=0\` or \`protocolVersion=2\` for older drivers\), or switch PgBouncer to session mode \(which limits concurrency\). The developer adds \`prepareThreshold=0\` and the errors stop.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T12:46:17.120367+00:00— report_created — created