prepared statement "s0" already exists
Fixes the Prisma error 'prepared statement already exists' (Postgres code 42P05) when connecting through PgBouncer in transaction mode, by adding pgbouncer=true to the connection string so Prisma stops using prepared statements. Use when queries fail with this error behind Supabase, Heroku, Railway, or any PgBouncer pooler. Not for direct connections or P2024 pool timeouts.
TL;DR: Add ?pgbouncer=true to your DATABASE_URL. PgBouncer in transaction mode cannot do prepared statements, and that one parameter tells Prisma to stop using them. Keep a separate direct connection (no pooler) for migrations.
Error: prepared statement "s0" already existsSteps
- Confirm you are behind PgBouncer in transaction mode: your connection string points at a pooler port (for example Supabase port 6543) or your provider says 'transaction mode'.
Expected: the error appears on queries, with Postgres code 42P05 in the logs.
- Add the Prisma PgBouncer flag to the app's connection string:
postgresql://YOUR_HOST:6543/postgres?pgbouncer=trueKeep your real username, password, and host; only the parameter is new. Expected: Prisma logs no longer prepare statements on this datasource.
- Keep migrations on a direct connection. Set a separate variable for them:
DIRECT_URL="postgresql://YOUR_HOST:5432/postgres" Expected: prisma migrate deploy runs against the direct port, the app runs against the pooler.
- Re-run the failing queries.
Expected: the 'already exists' errors stop.
When to use
- The error is
prepared statement ... already existswith code 42P05 and you connect through PgBouncer / Supavisor / a pooler - Supabase, Heroku, Railway, or any hosted Postgres with a transaction-mode pooler
When not to use
- Direct connections to Postgres: prepared statements work fine there, so this error means something else
- P2024 pool timeouts: that is pool exhaustion, not prepared statements
Compatibility
Prisma Client 4.x through 6.x. Any PgBouncer deployment in transaction pooling mode.
Variant phrasing: prepared statement "$z" already exists
Same error, different generated statement name. The fix is identical.
Variant phrasing: error only in production, not locally
Local dev usually connects directly (no pooler), so the bug only appears where the pooler sits. That mismatch is expected.
Why it happens
Prisma names its prepared statements per connection. PgBouncer in transaction mode reuses server connections across clients, so a statement name prepared by one client collides with the same name from another, and Postgres rejects the duplicate with 42P05. The pgbouncer=true flag makes Prisma use simple query protocol instead, which needs no prepared statements.
Edge cases
- Session-mode poolers (Supabase port 5432) support prepared statements; you only need the flag for transaction mode.
- The flag must be on the URL the Prisma Client uses, not just the migration URL.
- Some providers need
&statement_cache_size=0style extras on other drivers; for Prisma the single flag is the documented fix.
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.