Builders

    Supabase Maximum Number of Clients Reached Error

    A Supabase connection pooler hitting its max-clients limit in session mode, resolved by switching to transaction mode on port 6543 and reusing one client.

    The Supabase error about the maximum number of clients being reached means you have hit your connection pooler's client limit, almost always because too many database connections are open. The fix is to use the connection pooler correctly: switch to transaction mode, on port 6543, instead of session mode, on port 5432, for serverless or many short-lived connections, because session mode caps clients at your configured pool size while transaction mode reuses connections and supports far more. Just as important, reuse a single client instead of opening a new connection for every request or function call, which is the pattern that exhausts the pool. If you genuinely need more headroom, raise the pool size or max client connections in the dashboard, or move to a larger instance.

    Short answer

    This is a connection-handling problem, not a data or security one, so the fix is about how your app connects. Per Supabase's connecting to Postgres guide, the pooler offers transaction mode on port 6543 and session mode on port 5432, and transaction mode supports far more clients because it reuses connections, which is why it suits serverless. Per Supabase's connection management docs, in session mode clients are capped at your pool size, so exceeding it produces the max-clients error. So use the transaction-mode pooler for app traffic, reuse a single client, and raise the pool size only if you truly need to.

    What the error means

    Supabase puts a connection pooler in front of your Postgres database, and this error comes from that pooler telling you it has run out of client slots. Postgres itself can only handle a limited number of direct connections, so the pooler, currently Supavisor and previously PgBouncer, multiplexes many client connections onto fewer database connections. When the number of clients trying to use the pool exceeds its limit, you get a message that the maximum number of clients has been reached, often phrased as max clients reached in session mode.

    So the error is about connection capacity, not about your data, your keys, or your access rules. Nothing is broken in your schema or your security; you are simply opening more connections than the pool is configured to serve at once. That framing points you at how and where your app connects, which pooler mode it uses, and whether it reuses connections, rather than at anything in your database content. The rest of the fix follows from understanding the pooler's two modes.

    Session mode versus transaction mode

    The pooler has two modes, and choosing the right one is the core of the fix. In session mode, reached on port 5432 through the pooler, each client holds onto a database connection for its whole session, so the number of clients you can serve is capped at your pool size; if the pool size is fifteen, the fifteenth client is the last one served and the next hits the max-clients error. In transaction mode, reached on port 6543, a connection is handed back to the pool after each transaction and reused by the next client, so the same few database connections serve far more clients.

    This is why transaction mode is the right choice for serverless and high-connection workloads. Serverless functions on platforms like Edge Functions, Vercel, or AWS Lambda each try to connect, and there can be many at once, so session mode's per-client hold exhausts the pool quickly, while transaction mode's reuse handles the load. So if you are hitting the max-clients error from serverless or from many short-lived connections, moving from the session-mode port to the transaction-mode port is usually the single most effective change.

    Why AI-generated apps hit this

    Apps generated quickly, including AI-built ones, tend to hit this error because they open connections without managing them. A common pattern is creating a new database client or connection on every request or every function invocation instead of creating one and reusing it, so a burst of traffic opens a burst of connections and the pool fills. Generated code often does the simplest thing that works in a demo, which is a fresh connection each time, and that does not survive real concurrency.

    The other common cause is connecting directly to Postgres or using session mode from a serverless environment, where each invocation grabs and holds a connection. Neither is a flaw in Supabase; both are connection-handling habits that scale poorly. So if your app started throwing this error as traffic grew, look at whether it creates connections per request and which pooler mode it uses, because that is almost always where the exhaustion comes from, and it is fixable without changing your data or your platform.

    Fix: use transaction mode and the pooler

    The first fix is to use the transaction-mode pooler connection string, on port 6543, for your application traffic, especially anything serverless. This gives you the connection reuse that lets a small number of database connections serve many clients, which directly addresses the max-clients limit you are hitting in session mode. Point your app or your serverless functions at the transaction-mode pooler rather than at a direct database connection or the session-mode port.

    Also make sure you are using the pooler at all rather than connecting straight to Postgres. A direct connection to the database uses one of a small number of raw Postgres connection slots, which you exhaust quickly under load, whereas the pooler exists precisely to sit between your app and the database and manage those slots. So for normal application traffic, the pooler in transaction mode is the intended path, and reserving direct connections for migrations or admin tasks keeps your limited raw connections free.

    Fix: reuse the client and raise the pool size

    The second fix is in your code: create the Supabase client or database connection once and reuse it, rather than instantiating a new one per request. Reusing a single client means your app is not constantly opening and holding new connections, which is often enough on its own to stop the pool from filling. In a serverless function, initialize the client outside the handler so it is reused across invocations where the platform allows, rather than inside on every call.

    If, after using transaction mode and reusing connections, you still legitimately need more capacity, you can raise the limits. Supabase lets you set the pool size and the max client connections in your database settings, so increasing those gives the pooler more room, and moving to a larger instance raises the underlying Postgres connection limit. So the order is to fix the mode and the reuse first, which resolves most cases for free, and only raise the limits or upgrade if your genuine concurrency requires it.

    Causes and fixes at a glance

    Matching the cause to its fix makes this quick. The table below maps the common ones.

    CauseWhy the pool fillsFix
    Session mode from serverlessEach client holds a connectionUse transaction mode, port 6543
    New client per requestBursts of connections open at onceCreate one client and reuse it
    Direct connection under loadFew raw Postgres slots exhaustedConnect through the pooler
    Pool size too smallCapped at the configured sizeRaise pool size or max client connections
    Genuine high concurrencyMore load than the instance allowsUpgrade to a larger instance

    Read the first two rows first, since session mode from serverless and creating a client per request cause most max-clients errors.

    Direct connection versus the pooler

    It helps to know when to use each connection type, because mixing them up is a common source of this error. A direct connection goes straight to Postgres and uses one of its limited raw connection slots, which is fine for a migration, a one-off admin task, or a long-lived server that holds a single connection, but poor for many short-lived or serverless connections. The pooler, by contrast, is built to serve application traffic by sharing a small set of database connections among many clients.

    So the rule of thumb is to use the pooler, in transaction mode, for your app and serverless functions, and reserve direct connections for tooling that genuinely needs a persistent, direct link. If your connection string points at the direct database port for normal traffic, switching it to the transaction-mode pooler is frequently the fix.

    Fix checklist

    Working through these steps clears the max-clients error. The checklist below covers them.

    StepActionDone?
    Use the poolerConnect through the pooler, not directly[ ]
    Choose transaction modeUse port 6543 for serverless and app traffic[ ]
    Reuse the clientCreate one client and reuse it, not per request[ ]
    Initialize outside handlersIn serverless, reuse across invocations[ ]
    Raise limits if neededIncrease pool size or max client connections[ ]
    Upgrade if genuinely neededMove to a larger instance for real concurrency[ ]

    The step teams skip most is reusing the client, since creating a new connection per request is the habit that fills the pool regardless of configuration.

    Where a scan fits

    This is a database and connection-handling issue rather than a security one, so a security tool does not resolve it, but the same app is worth checking for the security problems that AI-generated code more commonly ships.

    A scanner like PTKD.com analyzes your app build and flags security issues such as an exposed service role key, access relied on from the client without server-side rules, and other misconfigurations, mapped to OWASP MASVS. To be clear about the boundary: PTKD does not tune your connection pool or change your database settings, which are your Supabase configuration. It is useful because an app that mishandles connections often mishandles secrets too, so once the pooling is fixed, a scan catches the security gaps that a connection error would have hidden.

    What to take away

    • The max-clients error means you have exceeded your connection pooler's client limit, not that anything is wrong with your data or security.
    • Session mode caps clients at your pool size, while transaction mode on port 6543 reuses connections and serves far more, which is why it suits serverless.
    • AI-generated apps commonly hit this by creating a new connection per request or using session mode from serverless, so reuse a single client and switch to transaction mode.
    • Use the pooler for application traffic and reserve direct connections for migrations and admin tasks that need a persistent link.
    • Raise the pool size or upgrade the instance only after fixing the mode and the reuse, and use a tool like PTKD.com to check the same app for security gaps.
    • #supabase
    • #connection pooling
    • #supavisor
    • #postgres
    • #serverless

    Frequently asked questions

    What does the Supabase max clients reached error mean?
    It means you have exceeded your connection pooler's client limit, so the pooler has run out of client slots to serve. Supabase puts a pooler, currently Supavisor, in front of Postgres to share a limited number of database connections among many clients, and this error appears when more clients try to use the pool than it is configured to serve at once. It is a connection-capacity problem, not an issue with your data, keys, or access rules, so the fix is about how and where your app connects, chiefly which pooler mode it uses and whether it reuses connections.
    What is the difference between session mode and transaction mode?
    In session mode, on port 5432, each client holds a database connection for its whole session, so the number of clients you can serve is capped at your pool size, and the next client past that cap hits the max-clients error. In transaction mode, on port 6543, a connection is returned to the pool after each transaction and reused by the next client, so the same few connections serve far more clients. Transaction mode supports higher throughput and more clients, which is why it is the right choice for serverless functions and other many-short-connection workloads.
    Does Supabase use PgBouncer for connection pooling?
    Supabase's connection pooler is now Supavisor, having previously used PgBouncer, but the concept is the same: the pooler sits in front of Postgres and multiplexes many client connections onto fewer database connections. It offers transaction mode on port 6543, which reuses connections across clients, and session mode on port 5432, where clients are capped at your pool size. So whether you saw older references to PgBouncer or current ones to Supavisor, the fix for the max-clients error is the same, use the transaction-mode pooler for app traffic and reuse connections.
    Why do AI-generated apps hit the max clients error?
    Because they tend to open connections without managing them. A common pattern is creating a new database client or connection on every request or serverless invocation instead of creating one and reusing it, so a burst of traffic opens a burst of connections and the pool fills. Generated code often does the simplest thing that works in a demo, a fresh connection each time, which does not survive real concurrency. The other cause is connecting directly to Postgres or using session mode from serverless, where each invocation grabs and holds a connection. Reusing a single client and using transaction mode fixes it.
    How do I fix the Supabase max clients reached error?
    Use the transaction-mode pooler connection string on port 6543 for your app and serverless functions, since it reuses connections and supports many more clients than session mode. Make sure you are connecting through the pooler rather than directly to Postgres. In your code, create the client once and reuse it rather than instantiating a new one per request, and in serverless functions initialize it outside the handler. If you still need more capacity after that, raise the pool size or max client connections in your database settings, or move to a larger instance.
    When should I use a direct connection instead of the pooler?
    Use a direct connection to Postgres only for tasks that genuinely need a persistent, direct link, such as a database migration, a one-off admin task, or a long-lived server that holds a single connection. A direct connection uses one of Postgres's limited raw connection slots, which you exhaust quickly under many short-lived or serverless connections. For normal application traffic, use the pooler in transaction mode, which is built to serve many clients over a shared set of connections. Getting this split right is often the difference between an app that scales and one that repeatedly hits the max-clients limit.

    Keep reading

    Scan your app in minutes

    Upload an APK, AAB, or IPA. PTKD returns an OWASP-aligned report with copy-paste fixes.

    Try PTKD free