Sorry, too many clients already

Every connection slot on the Postgres server is taken, usually by serverless functions that each open connections of their own.

Server logs and terminal
sorry, too many clients already

Also appears as

  • remaining connection slots are reserved for roles with the SUPERUSER attribute
  • remaining connection slots are reserved for non-replication superuser connections

Prompt to fix it

Our database refuses connections with "sorry, too many clients already" (or "remaining connection slots are reserved"). Find every place the app creates a database client or pool. Make it create one shared client per server instance instead of one per request, reuse it across hot reloads in development, and connect serverless code through the Supabase transaction pooler (port 6543) with prepared statements turned off for our driver. Keep pool sizes small. Don’t just raise max_connections; find and fix what opens the extra connections.

Paste into Layerwise or any AI coding tool.

What it means

Postgres allows a fixed number of open connections, set by max_connections, and holds a few back for admins. When the regular slots run out, new connections are refused with one of these messages. Smaller Supabase compute sizes allow fewer connections, so small projects hit this early.

Serverless hosting makes it easy to run out. On Vercel or Netlify, every function instance opens its own connections, and a traffic spike multiplies them. In development, hot reload can create a new database client on every file save without closing the old one.

The fix is a connection pooler: a service that keeps a few real connections open and shares them among many short requests. Supabase includes one, and its transaction mode on port 6543 is the one meant for serverless functions. Calls made through supabase-js go over HTTP to Supabase’s API, which has its own pool, so they open no database connections from your app.

Common causes

  • Serverless functions use the direct connection string on port 5432, so every instance opens its own connections.
  • The code creates a new client or pool inside the request handler and never closes it.
  • Hot reload in development creates a fresh Prisma or pg client on every save.
  • Each instance’s pool is large, like the pg default of 10, and many instances run at once.
  • A query or transaction never releases its connection, so open connections pile up.

How to fix it

  1. In Supabase, click Connect and copy the transaction pooler string (port 6543). Use it as the database URL for serverless code.
  2. Turn off prepared statements, which transaction mode doesn’t support: add ?pgbouncer=true to the URL for Prisma, or pass prepare: false to postgres.js.
  3. Create the database client once at module level and reuse it. In development, keep it on globalThis so hot reload doesn’t make another.
  4. Keep each serverless pool small, around one connection per instance.
  5. Run select usename, application_name, count(*) from pg_stat_activity group by 1, 2 order by 3 desc; to see what holds the connections.

Example fix

import postgres from 'postgres';

// One client per server instance, reused across hot reloads in development.
const globalForDb = globalThis as unknown as { sql?: postgres.Sql };

export const sql =
  globalForDb.sql ??
  // DATABASE_URL is Supabase's transaction pooler string (port 6543).
  postgres(process.env.DATABASE_URL!, {
    prepare: false, // transaction mode doesn't support prepared statements
    max: 1
  });

if (process.env.NODE_ENV !== 'production') globalForDb.sql = sql;
All errors
504: GATEWAY_TIMEOUT Code: FUNCTION_INVOCATION_TIMEOUT ID: iad1::q8n2v-1793091406762-4b1e2f9a7c3d

FUNCTION_INVOCATION_TIMEOUT

A Vercel Function ran past its time limit, so Vercel stopped it and returned a 504 in place of your response.

POST https://my-app.com/api/checkout 500 (Internal Server Error)

500 Internal Server Error

The server’s code threw an error or crashed while handling the request, and the real error message is waiting in the server logs.

GET http://localhost:3000/api/items net::ERR_CONNECTION_REFUSED

net::ERR_CONNECTION_REFUSED

Nothing was listening at that address and port, usually because the server isn’t running or the app is calling the wrong port.

new row violates row-level security policy for table "todos"

New row violates row-level security policy

Postgres refused to save a row because no row-level security policy on that table lets the current user write it.

Build with an agent that checks its work

The Layerwise agent clicks through the live preview and reads the console, so it catches errors before you do.