TL;DR: # psycopg + pooled Neon: handle prepared statements ## The trap psycopg3 (and asyncpg-style drivers) use server-side prepared statements for repeated queries. Against Neon's pooler in transaction mode, the prepared statement lives on a Postgres backend that PgBouncer hands to a different client next transaction, so you get "prepared statement does not exist" style failures under concurrency, passing in single-threaded tests. ## The rule Two supported options, from Neon's pooling semantics: 1.

## Fix
1. # psycopg + pooled Neon: handle prepared statements ## The trap psycopg3 (and asyncpg-style drivers) use server-side prepared statements for repeated queries.
   Expected: this specific failure stops.
2. Use the direct connection string for anything that relies on prepared statements.
   Expected: the request routes instead of erroring.
3. Or disable client-side preparation (psycopg3:.
   Expected: this specific failure stops.

## Details
Against Neon's pooler in transaction mode, the prepared statement lives on a Postgres backend that PgBouncer hands to a different client next transaction, so you get "prepared statement does not exist" style failures under concurrency, passing in single-threaded tests. ## The rule Two supported options, from Neon's pooling semantics: 1. 2. `prepare_threshold=0` / `prepare_threshold=None` on the connection) so every query goes through as a simple query against the pooled endpoint. ## Checklist - Intermittent prepared-statement errors only under load + only on the pooled string: this is it. - SQLAlchemy users: this is separate from `pool_pre_ping` ; you can need both. - `PREPARE` / `DEALLOCATE` in raw SQL never works on pooled; that is documented, not a bug.

## When to use
You hit exactly this: psycopg + Neon pooler: disable prepared statements or go direct in psycopg + Neon pooler.

## When not to use
A different error, or the same symptom in a different tool. This page only covers the failure above.

## Compatibility
psycopg + Neon pooler.

## Root cause
Session state survives because there is no pooler in the middle.