28000
Read PG 18 manual ↗invalid_authorization_specification
- SQLSTATE
- 28000
- Condition name
- invalid_authorization_specification
- Class
- Invalid Authorization Specification
- Source macro
- ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION
- Evidence
- Observed at runtime in the source evidence
English SQLSTATE atlas: authored explanations and source/runtime evidence are separate from the manual definitions. View source ↗
At a glance
SQLSTATE 28000 is invalid_authorization_specification in Class 28. 28000 is the authorization-specification class for rejected startup or authorization context. The selected missing-role startup path sends C=28000, S=FATAL, and role "<generated>" does not exist before a session is established; other fixed authentication paths use this class for concrete certificate, pg_hba, and LOGIN eligibility failures.
Meaning
This class covers authorization context rejected while a connection is being established or authenticated. The selected branch is the startup role lookup: the server cannot create a session for the requested role and sends a FATAL ErrorResponse. Certificate, pg_hba, and LOGIN-eligibility paths remain separate producers under the same class.
The selected role lookup happens before a backend can enter a normal session. That explains both the FATAL severity and the absence of session-level transaction state. Do not merge it with 28P01 (password authentication failure), 42501 (permission denied after a session exists), or 3D000 (database selection); the startup phase and the server fields identify which branch you are looking at.
Startup branch guide
| What to inspect | Selected missing-role branch | Other Class 28 possibilities |
|---|---|---|
| Server field | C=28000, S=FATAL, M=role "%s" does not exist |
Preserve the server's own code and primary; class membership alone is not enough. |
| Session state | No usable session and no transaction exists on the failed attempt | A later authorization check may occur after a session exists and has a different recovery boundary. |
| Repair | Create/rename the role as intended, or correct the startup user, then connect afresh | Correct the certificate, pg_hba.conf, LOGIN attribute, or mapping named by the actual diagnostic. |
Diagnosis
Use the server ErrorResponse or authentication log as SQLSTATE authority and classify the phase: role lookup, pg_hba rule, certificate, or LOGIN permission. The selected collector is a raw server ErrorResponse record, not csvlog/jsonlog; raw startup and psycopg are separate attempts. In the selected runs psycopg reports driver SQLSTATE null, the failed connection has no transaction, and two independent fresh known-role probes can still run SELECT 1; this is not a collector correlation claim.
For a missing role, compare the requested startup user with the role catalog from a separate administrative session and check for quoting or case-folding differences. Preserve the raw C, S, and M fields: psycopg's null is a property of its independent failed startup attempt, not evidence that the server omitted SQLSTATE. A fresh probe must use a separate connection and cannot turn the failed attempt into a transaction that can be rolled back.
Response
Correct the role, LOGIN/mapping, certificate, or pg_hba rule identified by the server, then establish a fresh connection. There is no failed session on which to issue ROLLBACK; do not infer 28000 from a client exception without its server field, and do not replay non-idempotent startup work blindly.
After a role or authentication change, reconnect with the exact intended user and database and verify the resulting identity from the new session. If the application had already sent work on another connection, reconcile that work separately; the pre-session FATAL itself does not establish a business transaction to retry.
Observed diagnostics
The selected startup role branch emits FATAL with primary role "%s" does not exist; the role name is dynamic. Other authorization failures in Class 28 can use different primary text. Treat the server ErrorResponse as authoritative because the client never receives a usable session SQLSTATE.
The selected server record is role "u28000_missing_fbc912bb7e6f" does not exist; the generated role name is run-specific. The raw ErrorResponse and the psycopg null belong to separate startup attempts, so they must not be combined as if they were two views of one session.
Representative case
The registry contains the probe used after the startup ErrorResponse. The trigger uses a generated role on a fresh startup connection, so no SQL statement can reproduce that pre-session failure.
SELECT 1;
The selected 18.6 server ErrorResponse is C=28000, S=FATAL, role "u28000_missing_fbc912bb7e6f" does not exist. The driver startup diagnostic has SQLSTATE null; the failed connection has no transaction, while the known-good probe returned 1 and IDLE.
The downloadable case and evidence projections are 28000 case JSON and authored evidence. The runner manifest is verify/cases/28000/cases.json; the page SQL is checked against its shared registry before publication.
Versions
The generated facts table records the locked catalogue snapshots and earliest observed definition. The selected natural runtime scope is PostgreSQL 18.6 and 10.21; this does not infer behavior for every intermediate release.
Sources
src.miscinit-missing-role.18.6—src/backend/utils/init/miscinit.catREL_18_6commit724edf9bde9d356724ad384a2e196edc3c9f80f7; fixed blob SHA-25654bdb859c143a6c70d86067ccf2e124f59aa0852e5705bb653652e41a60af39a(source).src.miscinit-missing-role.10.23—src/backend/utils/init/miscinit.catREL_10_23commit02991e79f8f58bc208f05dcc8af0c62dbe0a6ea4; fixed blob SHA-256c4d2e234f96644d43aad9e9a6728df693e7f75175f05a550701413ceee70fb82(source).src.calls.REL_18_6/src.calls.REL_10_23— fixed local call scans, SHA-2569ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf/00d16d3eb01b71ccf1b245c8f3102f9d0ec9f36fb02777b8dd1b99fcb263040c; these scans preserve the resolved call context used by the claims.
Source evidence
Evidence belongs to the frozen source and runtime versions listed here. It is not a runtime verification of the selected manual version.
28000 is the invalid_authorization_specification condition in Class 28.
Method: Read the fixed errcodes.txt definition and the locked catalogue metadata.
Limits: Directory identity does not identify every backend or client path.
The fixed source paths associated with 28000 report the condition in the mechanism selected for this page.
Method: Trace the resolved source call records at the fixed release commits and compare their dynamic message fields.
Limits: Other calls can retain the same SQLSTATE with different context or text.
src.miscinit-missing-role.18.6 src.miscinit-missing-role.10.23
The selected invalid_authorization case passed with the expected structured diagnostics, recovery assertions, and cleanup on PostgreSQL 18.6 and 10.21.
Method: Run the shared registry case on isolated runner-owned latest and PG10 targets; inspect the final summaries and raw results.
Limits: This covers the selected case and versions only; it does not generalize to every driver, proxy, or intermediate release.
The selected missing-role producer runs during startup, emits a FATAL ErrorResponse, and leaves no usable session or transaction; the server ErrorResponse is therefore authoritative over a client exception with SQLSTATE null.
Method: Compare the fixed miscinit.c source path with the selected raw server ErrorResponse and independent driver/probe observations.
Limits: This is the selected missing-role startup branch; other Class 28 producers can occur in different authentication phases and use different diagnostics.
src.miscinit-missing-role.18.6 runtime.28000-conn-contract-latest.latest
The locked catalogue records 28000 in the listed snapshots; the runtime comparison here is limited to PostgreSQL 18.6 and 10.21.
Method: Read the generated facts block and locked manifest, then compare the selected target summaries.
Limits: Presence in a definition file is not an exact behavioral introduction; the two runtime targets do not prove all middle versions.
- src/backend/utils/errcodes.txt · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 raw/calls/REL_18_6.jsonl· REL_18_6 ·raw/calls/REL_10_23.jsonl· REL_10_23 ·- src/backend/utils/init/miscinit.c · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 - src/backend/utils/init/miscinit.c · REL_10_23 ·
02991e79f8f58bc208f05dcc8af0c62dbe0a6ea4 - verify/cases/28000/cases.json · workspace ·
- verify/cases/28000/snippets.json · workspace ·
Message templates
explicit FATAL · message.missing-role
Primary
role "%s" does not exist
Reproduction & repair cases
invalid_authorization · PG 10, 18
Preconditions
- A runner-owned disposable target is provisioned.
Trigger: Attempt startup as a generated role that does not exist, then probe a known owner connection.
Expected assertions
- The selected server diagnostic has the expected SQLSTATE
- The selected recovery/probe assertions pass
- Runner-owned resources are cleaned up
Repair: Follow the case-specific repair statements and verify the resulting state.
Cleanup: Drop the case schema with an owner connection.
Recorded runtime evidence
18.6 (Homebrew) · passed
Run: 28000-conn-contract-latest
Disposable runner-owned target only; this is a bounded mechanism case, not a guarantee for every client or network path.
{
"probe": 1,
"collector": {
"primary": "role \"u28000_missing_fbc912bb7e6f\" does not exist",
"severity": "FATAL",
"sqlstate": "28000"
},
"diagnostic": {
"text": "connection failed: connection to server at \"127.0.0.1\", port 60047 failed: FATAL: role \"u28000_missing_fbc912bb7e6f\" does not exist",
"context": null,
"severity": null,
"sqlstate": null,
"table_name": null,
"column_name": null,
"schema_name": null,
"source_file": null,
"source_line": null,
"message_hint": null,
"datatype_name": null,
"exception_type": "OperationalError",
"internal_query": null,
"message_detail": null,
"constraint_name": null,
"message_primary": null,
"source_function": null,
"internal_position": null,
"statement_position": null,
"severity_nonlocalized": null
},
"missing_role": "u28000_missing_fbc912bb7e6f",
"probe_status": "IDLE",
"server_error": {
"C": "28000",
"F": "miscinit.c",
"L": "804",
"M": "role \"u28000_missing_fbc912bb7e6f\" does not exist",
"R": "InitializeSessionUserId",
"S": "FATAL",
"V": "FATAL"
},
"failed_connection_has_no_transaction": true
}10.21 (Debian 10.21-1.pgdg90+1) · passed
Run: 28000-conn-contract-pg10
Disposable runner-owned target only; this is a bounded mechanism case, not a guarantee for every client or network path.
{
"probe": 1,
"collector": {
"primary": "role \"u28000_missing_25282cee130f\" does not exist",
"severity": "FATAL",
"sqlstate": "28000"
},
"diagnostic": {
"text": "connection failed: connection to server at \"127.0.0.1\", port 60052 failed: FATAL: role \"u28000_missing_25282cee130f\" does not exist",
"context": null,
"severity": null,
"sqlstate": null,
"table_name": null,
"column_name": null,
"schema_name": null,
"source_file": null,
"source_line": null,
"message_hint": null,
"datatype_name": null,
"exception_type": "OperationalError",
"internal_query": null,
"message_detail": null,
"constraint_name": null,
"message_primary": null,
"source_function": null,
"internal_position": null,
"statement_position": null,
"severity_nonlocalized": null
},
"missing_role": "u28000_missing_25282cee130f",
"probe_status": "IDLE",
"server_error": {
"C": "28000",
"F": "miscinit.c",
"L": "518",
"M": "role \"u28000_missing_25282cee130f\" does not exist",
"R": "InitializeSessionUserId",
"S": "FATAL",
"V": "FATAL"
},
"failed_connection_has_no_transaction": true
}Definition snapshot: english-manuals:e60583a149f040c70b678181b08… · English manual source