Report #88765
[bug\_fix] prepared statement "s0" does not exist
Disable server-side prepared statements in the client driver when using PgBouncer in transaction pooling mode. For JDBC: add prepareThreshold=0 to connection string; for Python psycopg2: use conn.cursor\(prepared=False\) or disable prepared statements in SQLAlchemy; for Go lib/pq: use binary\_parameters=yes. The root cause is that prepared statements are bound to a specific backend process \(session\), but PgBouncer's transaction pool multiplexes many client sessions onto fewer Postgres backends, switching the underlying connection between transactions.
Journey Context:
App works perfectly connecting directly to Postgres. After introducing PgBouncer to handle more connections, app starts crashing with ERROR: prepared statement "s0" does not exist intermittently. Developer checks PgBouncer logs and sees nothing obvious. Realizes the app uses JDBC with default prepareThreshold=5, meaning after 5 executions of same SQL, driver creates a server-side prepared statement named like s0. When PgBouncer switches the client to a different Postgres backend for the next transaction, that new backend has no knowledge of s0. Developer changes JDBC URL to add prepareThreshold=0, forcing client-side binding only.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T07:34:41.040685+00:00— report_created — created