↑↓ select↵ open⌫ change scopeOpen full search

PG.CENTER connects PostgreSQL documentation, reference, and ecosystem knowledge. Maintained by Pigsty.

Wiki / SQLSTATE

53300 — too_many_connections

53300 is PostgreSQL's toomanyconnections condition in Class 53, insufficient_resources. It is emitted when a new backend cannot be admitted because a connection capacity limit has been reached. The representative case uses a role connection limit of one and opens a second session for that role.

English manual coverage begins with PostgreSQL 8.1. Source history and runtime observations have their own recorded version boundaries.

Reading PG 18·Select a recorded version

aliases
Unknown
class name
Insufficient Resources
condition name
too_many_connections
lang
en
sqlstate
53300

Definition PG 18

At a glance {#at-a-glance}

53300 is PostgreSQL's too_many_connections condition in Class 53, insufficient_resources. It is emitted when a new backend cannot be admitted because a connection capacity limit has been reached. The representative case uses a role connection limit of one and opens a second session for that role.

This is a connection-startup failure. No SQL transaction is opened for the rejected session. In the final runs, psycopg reported the startup exception text but exposed sqlstate=None; the PostgreSQL collector recorded the server's actual FATAL SQLSTATE 53300. Keep those observations separate when diagnosing a connection pool or an authentication gateway.

The case role_connection_limit passed on PostgreSQL 18.6 and the isolated PostgreSQL 10.21 target. The run IDs and per-case assertions are retained in the public evidence JSON.

Meaning and trigger paths {#meaning}

During backend startup, PostgreSQL checks the authenticated role's rolconnlimit after creating the backend bookkeeping entry. For a non-superuser role with a nonnegative limit, a count above that limit raises FATAL with ERRCODE_TOO_MANY_CONNECTIONS and the message too many connections for role "%s". The source comments describe this role count as approximate because simultaneous startups can race.

The same SQLSTATE can identify another capacity path, such as a server-wide client limit. The role-limit case here does not establish the cause of every 53300; use the primary message, collector fields, and the configured role, database, and server limits to classify it.

Because the failure occurs before the SQL protocol reaches a transaction, transaction status is not INERROR or IDLE for the rejected session. A management or pool connection that remains open can change the limit and then prove recovery with a new session.

Messages and diagnostics {#messages}

The executable excerpt uses the same role-limit SQL statements as the runner. limited_user is replaced by a disposable role in the isolated run. The second connection is a separate client-startup operation, so it is described between the SQL statements rather than represented by a fabricated SQL command.

ALTER ROLE limited_user CONNECTION LIMIT 1;
-- Keep one session open as limited_user.
-- Open a second session as limited_user: startup returns FATAL 53300.
ALTER ROLE limited_user CONNECTION LIMIT -1;
SELECT 1;

The latest target's collector record was:

SQLSTATE: 53300                 # csvlog/jsonlog sql_state_code
severity: FATAL
message_primary: too many connections for role "<generated-role>"
source: miscinit.c / InitializeSessionUserId / line 880
driver startup sqlstate: null
transaction: none opened
repair: reset CONNECTION LIMIT; fresh role connection SELECT 1 -> 1

PostgreSQL 10.21 produced the same message and SQLSTATE in its collector, with the source location miscinit.c:575. In both targets the known role connection succeeded after the limit was reset. The role name and port above are run-generated values; the collector's sql_state_code is the server evidence. A startup error may not expose a SQLSTATE property through a particular driver.

Diagnosis {#diagnosis}

Capture the connection parameters without secrets, the role and database, the server version, the exact startup text, and a collector record correlated by time, user, and connection source. Check pg_roles.rolconnlimit, database and server connection settings, and current backend counts. A pool can exhaust a role limit while the server still has global capacity.

Treat the role count as a capacity observation rather than an exact admission proof under a burst: PostgreSQL documents an approximate check for concurrent startups. Check the primary message before changing a global setting, and preserve an administrative path for recovery.

Do not issue transaction cleanup commands for the rejected session. It never reached a usable SQL transaction. A still-open owner or administration connection can restore the setting; the meaningful repair assertion is a fresh login for the affected role followed by a real query.

Response and repair {#response}

  • Bound pool size and connection concurrency so a role does not repeatedly exhaust its limit.
  • Increase the role, database, or server capacity only after checking memory, workload, and the intended admission policy.
  • Keep reserved capacity and a controlled administrative connection for recovery; do not grant superuser status merely to bypass a limit.
  • After changing the limit, open a new connection and execute a harmless query. Reconcile pool state and close stale sessions before retrying application work.

The representative repair reset the disposable role's limit, opened a new role connection, and observed SELECT 1 as 1. That proves recovery of this role-limit path; it does not prove that a different database-wide or server-wide capacity failure has been repaired.

Versions and boundaries {#versions}

The catalogue has a definition-presence observation for 53300 at PostgreSQL 7.4 and through the locked 8.4.22 pre-9.0 definitions, then in every listed formal snapshot through PostgreSQL 18.6 and the PostgreSQL 19 Beta 3 preview. This is a definition-only presence boundary, not an exact implementation introduction or runtime-use claim. The class title change between the 9.0 header and 9.1 text definitions is recorded in the catalogue; it is not an asserted implementation change for this condition.

The role-limit case passed on PostgreSQL 18.6 and 10.21. Source line numbers differ, and the driver/collector split is part of the observed boundary. Database-wide and server-wide capacity paths, connection pool behavior, and operating-system resource exhaustion require their own evidence.

Related {#related}

28P01 — invalid_password is another connection-startup failure with no SQL transaction. 57014 — query_canceled is a statement interruption after a session has connected. 42P01 — undefined_table is a SQL name-resolution error after connection startup.

Sources {#sources}

Structured evidence is recorded in the public evidence JSON. Source records are fixed to PostgreSQL commit 724edf9bde9d356724ad384a2e196edc3c9f80f7; runtime records retain the collector output, driver observation, both target IDs, and structured observations.

Compare versions

SQLSTATE evidence and recorded runtime versions

cases · role_connection_limit
{
  "assertions": [
    "The new connection receives FATAL SQLSTATE 53300",
    "The existing owner connection can restore the limit and continue"
  ],
  "case_id": "role_connection_limit",
  "cleanup": "Restore the role limit, drop the role, and drop the case schema.",
  "has_snippet": true,
  "position": 0,
  "preconditions": [
    "A disposable login role",
    "The role connection limit is set to one while one session is already connected"
  ],
  "repair": "Pool and bound connections, reserve capacity for administration, and investigate the role/database/server limit that was reached.",
  "trigger": "Open a second connection for that role.",
  "versions": [
    "10",
    "18"
  ]
}
claims · identity.class-and-condition
{
  "claim_id": "identity.class-and-condition",
  "limits": "Directory identity does not identify whether a role, database, or server-wide capacity limit caused an occurrence.",
  "method": "Read the Class 53 section and 53300 row in the frozen errcodes.txt snapshot.",
  "position": 0,
  "runtime": [],
  "sources": [
    "src.errcodes.18.6"
  ],
  "statement": "53300 is the too_many_connections condition in Class 53 insufficient_resources."
}
claims · mechanism.role-connection-limit
{
  "claim_id": "mechanism.role-connection-limit",
  "limits": "This source path is role-specific; other capacity paths can use 53300 with different messages.",
  "method": "Read the role connection-limit branch, explicit ERRCODE_TOO_MANY_CONNECTIONS, message construction, and concurrency comment.",
  "position": 1,
  "runtime": [],
  "sources": [
    "src.miscinit.18.6"
  ],
  "statement": "During backend startup, a non-superuser role whose CountUserBackends result exceeds rolconnlimit receives FATAL SQLSTATE 53300 with too many connections for role \"%s\"; the source documents the count check as approximate under a race."
}
claims · mechanism.server-capacity
{
  "claim_id": "mechanism.server-capacity",
  "limits": "This is a source-confirmed server-wide startup gate; it is not covered by the role-limit runtime case and does not classify database or operating-system resource failures.",
  "method": "Read the CAC_TOOMANY backend-startup branch in the fixed source.",
  "position": 2,
  "runtime": [],
  "sources": [
    "src.backend-startup.18.6"
  ],
  "statement": "The server-wide startup capacity gate also reports SQLSTATE 53300 at FATAL with sorry, too many clients already."
}
claims · runtime.driver-collector-boundary
{
  "claim_id": "runtime.driver-collector-boundary",
  "limits": "Driver exposure can vary by client and startup failure; the collector record is the server-side SQLSTATE evidence here.",
  "method": "Compare the driver diagnostic, CSV collector state_code, message, severity, and source location in both final summaries and raw records.",
  "position": 3,
  "runtime": [],
  "sources": [
    "doc.protocol.18",
    "runtime.53300-registry-final-20260909.latest",
    "runtime.53300-registry-final-20260909.pg10"
  ],
  "statement": "For the rejected startup connection, psycopg exposed startup text with sqlstate null while the PostgreSQL collector recorded FATAL SQLSTATE 53300 and the role-limit message on both targets."
}
claims · runtime.capacity-repair
{
  "claim_id": "runtime.capacity-repair",
  "limits": "The repair proves the role-limit path only; it does not prove recovery of another global capacity limit.",
  "method": "Keep one holder session, trigger the second startup, restore the limit through an owner connection, and assert a new role session and query.",
  "position": 4,
  "runtime": [],
  "sources": [
    "case-manifest.53300",
    "snippet-registry.53300.final",
    "runtime.53300-registry-final-20260909.latest",
    "runtime.53300-registry-final-20260909.pg10"
  ],
  "statement": "After resetting the disposable role's connection limit, a fresh connection for that role executed SELECT 1 and returned 1 on PostgreSQL 18.6 and 10.21."
}
claims · versions.catalogue-boundary
{
  "claim_id": "versions.catalogue-boundary",
  "limits": "The first scanned release is a lower bound, not an asserted introduction version.",
  "method": "Read the manifest snapshots and definition references for the code.",
  "position": 5,
  "runtime": [],
  "sources": [
    "manifest.53300"
  ],
  "statement": "The locked catalogue records 53300 in every listed formal snapshot from 9.0.23 through 18.6 and in 19beta3; pre-9.0 history is not scanned."
}
messages · message.server-capacity
{
  "limits": "This template belongs to the server-wide startup gate and is distinct from the role-limit template.",
  "message_id": "message.server-capacity",
  "path": "",
  "position": 0,
  "raw": {
    "id": "message.server-capacity",
    "limits": "This template belongs to the server-wide startup gate and is distinct from the role-limit template.",
    "primary_template": "sorry, too many clients already",
    "severity_source": "explicit FATAL",
    "sources": [
      "src.backend-startup.18.6"
    ],
    "sqlstate": "53300"
  },
  "severity": "explicit FATAL",
  "sources": [
    "src.backend-startup.18.6"
  ],
  "templates": [
    {
      "kind": "primary",
      "literal": "sorry, too many clients already",
      "position": 0,
      "role": "",
      "template": "sorry, too many clients already"
    }
  ]
}
messages · message.role-limit
{
  "limits": "The role name is dynamically inserted; database-wide and server-wide capacity branches may use another primary template.",
  "message_id": "message.role-limit",
  "path": "",
  "position": 1,
  "raw": {
    "id": "message.role-limit",
    "limits": "The role name is dynamically inserted; database-wide and server-wide capacity branches may use another primary template.",
    "primary_template": "too many connections for role \"%s\"",
    "severity_source": "explicit FATAL",
    "sources": [
      "src.miscinit.18.6"
    ],
    "sqlstate": "53300"
  },
  "severity": "explicit FATAL",
  "sources": [
    "src.miscinit.18.6"
  ],
  "templates": [
    {
      "kind": "primary",
      "literal": "too many connections for role \" \"",
      "position": 0,
      "role": "",
      "template": "too many connections for role \"%s\""
    }
  ]
}
runtimes · runtime.53300-registry-final-20260909.latest · 18.6 (Homebrew) · passed
{
  "cases": [
    "role_connection_limit"
  ],
  "limits": "",
  "position": 0,
  "raw": {
    "cases": [
      "role_connection_limit"
    ],
    "id": "runtime.53300-registry-final-20260909.latest",
    "observed": {
      "collector_message": "too many connections for role \u003cgenerated-role\u003e",
      "collector_severity": "FATAL",
      "collector_source": "miscinit.c:880",
      "collector_sqlstate": "53300",
      "driver_sqlstate": null,
      "driver_text": "FATAL: too many connections for role \u003cgenerated-role\u003e",
      "restored_query": 1,
      "transaction": "none opened for rejected startup"
    },
    "raw": "verify/results/53300-registry-final-20260909/latest/raw.jsonl",
    "raw_sha256": "5fbcf85150b62d596ca4b2356639fdcd89ca2a910447e2be9029b90de108a635",
    "run_id": "53300-registry-final-20260909",
    "server_version": "18.6 (Homebrew)",
    "server_version_num": 180006,
    "snippet_registry": {
      "path": "verify/cases/53300/snippets.json",
      "sha256": "a83d740917ef53c3bf24494c12fe87f4c9cbb38f1e23c17991f2431ba13fa243"
    },
    "status": "passed",
    "summary": "verify/results/53300-registry-final-20260909/latest/summary.json",
    "summary_sha256": "e5505e6d644d9f68ba723c9af63fe069e69d030d1f554b8e8d37316b441f55a9",
    "target": "latest"
  },
  "run_id": "53300-registry-final-20260909",
  "runtime_id": "runtime.53300-registry-final-20260909.latest",
  "server_version": "18.6 (Homebrew)",
  "status": "passed",
  "target": "latest"
}
runtimes · runtime.53300-registry-final-20260909.pg10 · 10.21 (Debian 10.21-1.pgdg90+1) · passed
{
  "cases": [
    "role_connection_limit"
  ],
  "limits": "",
  "position": 1,
  "raw": {
    "cases": [
      "role_connection_limit"
    ],
    "id": "runtime.53300-registry-final-20260909.pg10",
    "image": "postgres@sha256:b2baf8998630663d21370da06387c950e587071bdd307ee34e661cdcc7442bcc",
    "observed": {
      "collector_message": "too many connections for role \u003cgenerated-role\u003e",
      "collector_severity": "FATAL",
      "collector_source": "miscinit.c:575",
      "collector_sqlstate": "53300",
      "driver_sqlstate": null,
      "driver_text": "FATAL: too many connections for role \u003cgenerated-role\u003e",
      "restored_query": 1,
      "transaction": "none opened for rejected startup"
    },
    "raw": "verify/results/53300-registry-final-20260909/pg10/raw.jsonl",
    "raw_sha256": "b03ea276f638c91efadd0791a9818993fc21620ec27ef643c60b7a9904fac6c3",
    "run_id": "53300-registry-final-20260909",
    "server_version": "10.21 (Debian 10.21-1.pgdg90+1)",
    "server_version_num": 100021,
    "snippet_registry": {
      "path": "verify/cases/53300/snippets.json",
      "sha256": "a83d740917ef53c3bf24494c12fe87f4c9cbb38f1e23c17991f2431ba13fa243"
    },
    "status": "passed",
    "summary": "verify/results/53300-registry-final-20260909/pg10/summary.json",
    "summary_sha256": "2ed0896c1548c0a3115e55381e03b0ccd392abe241552b574c64e88cca052f59",
    "target": "pg10"
  },
  "run_id": "53300-registry-final-20260909",
  "runtime_id": "runtime.53300-registry-final-20260909.pg10",
  "server_version": "10.21 (Debian 10.21-1.pgdg90+1)",
  "status": "passed",
  "target": "pg10"
}
sources · src.errcodes.18.6
{
  "commit": "724edf9bde9d356724ad384a2e196edc3c9f80f7",
  "docs_url": "",
  "kind": "",
  "location": "lines 407-414",
  "path": "src/backend/utils/errcodes.txt",
  "position": 0,
  "sha256": "6e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba",
  "source_id": "src.errcodes.18.6",
  "tag": "REL_18_6",
  "url": "https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/errcodes.txt"
}
sources · src.backend-startup.18.6
{
  "commit": "724edf9bde9d356724ad384a2e196edc3c9f80f7",
  "docs_url": "",
  "kind": "",
  "location": "lines 340-344",
  "path": "src/backend/tcop/backend_startup.c",
  "position": 1,
  "sha256": "5516f3d6d83ae6fc66ca6bad9a7cea3cd2f8de53f0597c26baa2bd224ac270bb",
  "source_id": "src.backend-startup.18.6",
  "tag": "REL_18_6",
  "url": "https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/tcop/backend_startup.c#L340-L344"
}
sources · src.miscinit.18.6
{
  "commit": "724edf9bde9d356724ad384a2e196edc3c9f80f7",
  "docs_url": "",
  "kind": "",
  "location": "lines 868-880",
  "path": "src/backend/utils/init/miscinit.c",
  "position": 2,
  "sha256": "54bdb859c143a6c70d86067ccf2e124f59aa0852e5705bb653652e41a60af39a",
  "source_id": "src.miscinit.18.6",
  "tag": "REL_18_6",
  "url": "https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/init/miscinit.c#L868-L880"
}
sources · doc.protocol.18
{
  "commit": "724edf9bde9d356724ad384a2e196edc3c9f80f7",
  "docs_url": "",
  "kind": "",
  "location": "ErrorResponse fields",
  "path": "doc/src/sgml/protocol.sgml",
  "position": 3,
  "sha256": "745339e07ebbc2bb67d98b258e25772ada644ef26011e3cd6b3fcbb435235f89",
  "source_id": "doc.protocol.18",
  "tag": "PG18-docs",
  "url": "https://www.postgresql.org/docs/18/protocol-error-fields.html"
}
sources · manifest.53300
{
  "commit": "",
  "docs_url": "",
  "kind": "local_artifact",
  "location": "snapshots and definition_blobs entries for the 53300 definition",
  "path": "sources/manifest.lock.json",
  "position": 4,
  "sha256": "1727a275f336988ff96b4f9990a4ca253080f73fc5d316e7def165c8cf3708a8",
  "source_id": "manifest.53300",
  "tag": "",
  "url": "../sources/manifest.lock.json"
}
sources · case-manifest.53300
{
  "commit": "",
  "docs_url": "",
  "kind": "local_artifact",
  "location": "role_connection_limit",
  "path": "verify/cases/53300/cases.json",
  "position": 5,
  "sha256": "631e5ca3a0056fe00b6d1495ddbe4a7880a5d4c09b1520c2ee972859d9174d4d",
  "source_id": "case-manifest.53300",
  "tag": "",
  "url": "../verify/cases/53300/cases.json"
}
sources · snippet-registry.53300.final
{
  "commit": "",
  "docs_url": "",
  "kind": "local_artifact",
  "location": "role_connection_limit ordered SQL",
  "path": "verify/cases/53300/snippets.json",
  "position": 6,
  "sha256": "a83d740917ef53c3bf24494c12fe87f4c9cbb38f1e23c17991f2431ba13fa243",
  "source_id": "snippet-registry.53300.final",
  "tag": "",
  "url": "../verify/cases/53300/snippets.json"
}

Source references

Full definition and evidence JSON

Definition sources

center · PostgreSQL 18 · english-manuals:ef69e2e58a5dd983283ff6c6091d86a6169973084e94f1cfd1ee1970a7308797

Text language: en · english-manuals:ef69e2e58a5dd983283ff6c6091d86a6169973084e94f1cfd1ee1970a7308797