Quick answer
PostgreSQL hit its max_connections limit (100 by default) and is refusing new connections. The durable fix is a connection pool — PgBouncer in front of Postgres, or an app-level pool with a fixed max size — so a few real connections are reused instead of one per request. Raising max_connections is a stopgap that costs RAM.
The exact error string
psql: error: connection to server failed:
FATAL: sorry, too many clients already
# application drivers surface the same thing:
# psycopg2.OperationalError: FATAL: sorry, too many clients already
# error: sorry, too many clients already (node-postgres)
# the OTHER message for a full server — a regular role hits this one FIRST:
FATAL: remaining connection slots are reserved for roles with the SUPERUSER attribute
Every connection slot is in use. Postgres allows max_connections simultaneous connections (default 100); once they are all taken, the next client is rejected. This is about the number of open connections, not the queries themselves.
Note the second message in the block above. The two are not interchangeable, and which one you get narrows the problem considerably — see which message you got.
Step 1: see what is holding connections
Before changing anything, look at who is connected. A pile of idle connections is the classic sign of a leak:
-- how many connections, grouped by state
SELECT count(*), state FROM pg_stat_activity GROUP BY state;
-- the configured ceiling
SHOW max_connections;
-- who is connected, and to what
SELECT pid, usename, application_name, state, query
FROM pg_stat_activity ORDER BY state;
Lots of idle rows → connections opened and never closed. idle in transaction → transactions left open (a missing commit/rollback).
Which message you got, and what it rules out
A full Postgres server produces two different refusals, and most write-ups treat them as synonyms. They are not: there is a band of connections where a regular application role is already being refused while the server still has slots left. Knowing which message you have tells you whether you are at the real ceiling or at the reserved one.
Verified on PostgreSQL 16.15 with max_connections = 10 and the default superuser_reserved_connections = 3, using a plain LOGIN role:
# ten connection attempts from a NON-superuser role:
succeeded: 7 refused: 3
# and what the three refusals said:
FATAL: remaining connection slots are reserved for roles with the SUPERUSER attribute
The wall for an ordinary role is max_connections − superuser_reserved_connections — seven here, not ten. If you size a pool against max_connections you will be three short of your own arithmetic and the error will arrive earlier than you predicted.
| Message | What is actually true | What it rules out |
|---|---|---|
remaining connection slots are reserved for roles with the SUPERUSER attribute |
The non-reserved slots are gone. The server still has its superuser reserve. | You can still get in as a superuser to diagnose. The reserve is doing its job. |
sorry, too many clients already |
Every slot is gone, reserve included. | Nothing is left for an admin session — and something is consuming the reserve. |
With all seven app slots held, a superuser connected without trouble and could read pg_stat_activity normally:
usename | count | max
----------+-------+--------
app | 7 | active
postgres | 1 | active
Which leads to the thing worth changing before you need it. In a separate run the connection holders were themselves connected as postgres — a superuser. They consumed all ten slots, the reserve included, and the next superuser connection was refused with sorry, too many clients already. Step 1 above asks you to connect and look at pg_stat_activity; that is exactly the moment you discover you cannot.
So if your application connects as a superuser — which is the default in a surprising number of Docker Compose files, migration runners and quick-start guides — superuser_reserved_connections protects nothing, including your ability to investigate. Giving the application a non-superuser role is worth doing on that basis alone, independently of least-privilege arguments: it keeps the reserve genuinely in reserve, and it converts the error you get into the more informative of the two.
Reproduced on PostgreSQL 16.15 (postgres:16-alpine) with max_connections=10 to make the ceiling reachable; the ratio, not the absolute numbers, is the point. reserved_connections (added in PostgreSQL 16, default 0) subtracts from the non-superuser pool in the same way if you set it.
Fix 1: use a connection pool (the real fix)
The root cause is almost always "a new connection per request/worker." A pool keeps a small, fixed set of connections and reuses them. Set a sane max in your app's pool:
// node-postgres: one shared pool, capped
import { Pool } from 'pg';
const pool = new Pool({ max: 10 }); // not a client per request
const { rows } = await pool.query('SELECT 1');
# Python / SQLAlchemy: bounded pool
engine = create_engine(url, pool_size=10, max_overflow=5)
For many app instances (or serverless), put PgBouncer between the app and Postgres in transaction mode — hundreds of clients then share a few dozen real connections.
Fix 2: close what you open
If you create connections manually, always release them — use a context manager or finally so an exception can't leak the connection:
# ✅ context manager returns the connection even on error
with psycopg2.connect(dsn) as conn:
with conn.cursor() as cur:
cur.execute("SELECT 1")
// ✅ always release a pooled client
const client = await pool.connect();
try { await client.query('SELECT 1'); }
finally { client.release(); }
Fix 3: serverless needs a pooler
On Lambda, Vercel, or Cloud Functions, each concurrent instance opens its own connections — hundreds of instances exhaust Postgres instantly. Don't connect directly; route through a pooler: PgBouncer, RDS Proxy, or a provider pooler (Supabase, Neon). Use the pooled connection string, not the direct one.
Fix 4: raise max_connections (last resort)
Only after pooling. Each connection reserves memory, so a high limit can exhaust RAM:
-- postgresql.conf (requires a restart)
max_connections = 200
-- then verify
SHOW max_connections;
Debugging checklist
- ✓ Count connections by state:
pg_stat_activity— manyidlemeans a leak - ✓ Add or fix a connection pool with a bounded max size
- ✓ Release connections in
finally/ a context manager - ✓
idle in transaction? Commit or roll back — don't leave transactions open - ✓ Serverless? Route through PgBouncer / RDS Proxy / a provider pooler
- ✓ Raise
max_connectionsonly as a stopgap (it costs RAM per connection)
Frequently Asked Questions
What does 'FATAL: sorry, too many clients already' mean?
PostgreSQL has reached its max_connections limit (100 by default) and refuses any new connection. Every existing slot is in use, so the next client to connect is rejected with this FATAL error. It is a connection-count problem, not a query problem.
What is the real fix?
Use a connection pool so your app reuses a small, fixed set of connections instead of opening a new one per request. PgBouncer in front of Postgres, or an application pool (SQLAlchemy, HikariCP, pg Pool) with a sensible max size, is the durable fix. Raising max_connections is a stopgap that costs RAM per connection.
How do I see what is using the connections?
Query pg_stat_activity: SELECT count(*), state FROM pg_stat_activity GROUP BY state;. A large number of idle connections points to a leak — connections opened and never closed or returned to the pool. idle in transaction points to transactions left open.
Why does it happen on serverless (Lambda, Vercel)?
Each serverless instance opens its own database connections, and under load there can be hundreds of concurrent instances — each multiplying connections until Postgres is exhausted. Put a pooler between them and the database: PgBouncer in transaction mode, RDS Proxy, or a provider pooler (Supabase, Neon).
Should I just raise max_connections?
Only as a temporary measure. Each connection reserves memory (work_mem and more), so raising max_connections too high can exhaust RAM and slow the whole server. Pooling lets a few hundred clients share a few dozen real connections, which is far more efficient than a high max_connections.
What is 'remaining connection slots are reserved'?
Postgres reserves a few slots (superuser_reserved_connections, default 3) for superusers so an admin can still log in during an outage. The two messages are not interchangeable. A regular role that exhausts the non-reserved slots gets exactly this message, remaining connection slots are reserved for roles with the SUPERUSER attribute — so its real wall is max_connections minus superuser_reserved_connections, verified as 7 of 10 on PostgreSQL 16.15. sorry, too many clients already means every slot including the reserve is gone. The catch is that the reserve only helps if your application connects as a non-superuser; if the app is itself a superuser it can consume the reserve and lock you out of your own diagnosis.
References
- Connection Settings (PostgreSQL docs)
- PgBouncer (pgbouncer.org)
More backend & build errors
Browse the full reference for Node.js, Python, Docker, and database errors — exact message, cause, and fix.