FATAL: sorry, too many clients already

Quick answer

PostgreSQL hit its max_connections limit (100 by default) and is refusing new connections. The durable fix is a connection poolPgBouncer 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)

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.

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).

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

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 connection slots (superuser_reserved_connections) for superusers so admins can still log in during an outage. Regular users see 'too many clients already' once non-reserved slots are full, while a superuser can still connect to investigate.

More backend & build errors

Browse the full reference for Node.js, Python, Docker, and database errors — exact message, cause, and fix.

All Error References Postgres: deadlock detected HTTP Status Codes
About the author

Pasindu Ishan is a software developer based in Sri Lanka. He builds privacy-first developer tools at JSON Dev Tools.