Report #7447
[bug\_fix] prepared statement "X" does not exist \(with PgBouncer transaction pooling\)
Switch PgBouncer to session pooling mode, or disable server-side prepared statements in the driver \(e.g., psycopg2 prepare\_threshold=None\), or use protocol-level unnamed prepared statements. Root cause: Named prepared statements \(PREPARE name AS ...\) are bound to the specific backend session/connection. PgBouncer's transaction pooling mode shares one backend across multiple client sessions; Client A prepares 'stmt1', Client B gets assigned the same backend and tries to execute 'stmt1' which doesn't exist in its session scope.
Journey Context:
App works locally but fails in staging with 'prepared statement "\_\_asyncpg\_stmt\_4\_\_" does not exist' only when under load via PgBouncer. Developers check PgBouncer logs and realize transaction pooling is enabled. They learn that asyncpg \(or psycopg2 with prepare\_threshold\) automatically uses named prepared statements for performance. When PgBouncer assigns the same Postgres backend to a different app request mid-transaction, the prepared statement handle is invalid. The fix is setting PgBouncer pool\_mode=session \(sacrificing some efficiency\) or configuring the driver to disable named prepared statements \(e.g., asyncpg cache\_prepared\_statements=False\).
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T02:44:03.115888+00:00— report_created — created