# 28P01 — invalid_password

> PostgreSQL reports SQLSTATE 28P01 when password authentication fails during connection startup. Check the matched pg_hba.conf rule and role secret, then prove recovery with a fresh connection.
---

## At a glance {#at-a-glance}

`28P01` is PostgreSQL's `invalid_password` condition in Class 28, `invalid_authorization_specification`. It identifies a password authentication failure while a client is establishing a session. The authentication method, the matched `pg_hba.conf` rule, and the role's stored secret determine the precise path.

This failure occurs before SQL execution. The rejected session has no transaction to roll back. In the final runs, psycopg reported the startup exception text but exposed `sqlstate=None`; the PostgreSQL collector recorded the server's `FATAL` SQLSTATE `28P01`. A driver-side exception without a code must not be described as if the client received the collector field.

The case `wrong_password_authentication` enabled a disposable `md5` rule, tried a wrong password, verified that the known password could open a new session and run `SELECT 1`, restored the exact HBA configuration, and verified a fresh management connection. It passed on PostgreSQL 18.6 and isolated PostgreSQL 10.21. The run IDs and assertions are retained in the [public evidence JSON](../data/evidence/28p01.json).

<!-- BEGIN SQLSTATE FACTS: generated by scripts/generate.py; do not edit -->

| Field | Value |
| --- | --- |
| SQLSTATE | `28P01` |
| Condition | `invalid_password` |
| Status | `active` |
| Known present by | `9.0.0` |
| Locked snapshots | `9.0.23, 9.1.24, 9.2.24, 9.3.25, 9.4.26, 9.5.25, 9.6.24, 10.23, 11.22, 12.22, 13.23, 14.24, 15.19, 16.15, 17.11, 18.6, 19beta3` |
| Macros | `ERRCODE_INVALID_PASSWORD` |
| Aliases | `—` |

<!-- source facts: data/errcodes/28P01.json -->
<!-- END SQLSTATE FACTS -->

## Meaning and trigger paths {#meaning}

The server selects an authentication method from the first matching `pg_hba.conf` rule. For password, MD5, and SCRAM paths that reject the supplied secret, `auth.c` selects `ERRCODE_INVALID_PASSWORD` and reports a `FATAL` message. The primary template is `password authentication failed for user "%s"`; the server can add HBA matching information to its log detail.

`28P01` does not distinguish a wrong password from every other authentication configuration problem. A missing role, a role that cannot log in, a certificate failure, or an HBA rule that selects another method can use a different SQLSTATE or message. The matched rule and the authentication method are part of the diagnosis.

The failure is at connection startup, before a backend can accept SQL. A pool should discard the rejected connection attempt and obtain a new one after the secret or HBA configuration is repaired. Existing owner or administration connections are useful for restoring a temporary rule, but reusing one of them does not prove that the affected role can authenticate.

## Messages and diagnostics {#messages}

The SQL statements below are the same password setup and probe statements used by the runner. `known_user` and `example-known-secret` are replaced by disposable values during the run. Editing `pg_hba.conf` and opening the wrong-password connection are startup operations, so they are described between the SQL statements rather than represented by a fake `RAISE` or SQL error.

<!-- BEGIN SQLSTATE SNIPPET: wrong_password_authentication -->
```sql
ALTER ROLE known_user PASSWORD 'example-known-secret';
-- Put a temporary first pg_hba.conf rule for host all all 0.0.0.0/0 md5.
-- Connect as known_user with a wrong password: startup returns FATAL 28P01.
-- Connect again with the known password and run:
SELECT 1;
-- Restore the original pg_hba.conf, open a fresh management connection, and run:
SELECT 1;
```
<!-- END SQLSTATE SNIPPET -->

The latest target's collector record was:

```text
SQLSTATE: 28P01
severity: FATAL
message_primary: password authentication failed for user "<generated-role>"
detail (collector): Connection matched file "<pg_hba.conf path>" line 1: "host all all 0.0.0.0/0 md5"
source: auth.c / auth_failed / line 320
driver startup sqlstate: null
transaction: none opened
repair: known-password SELECT 1 -> 1; restore HBA; fresh owner SELECT 1 -> 1
```

PostgreSQL 10.21 emitted the same primary message and SQLSTATE in its collector, with `auth.c:329` as the source location; its collector detail uses the older `pg_hba.conf line` wording and also includes a password-mismatch line. In both targets the known password authenticated under the temporary rule, the original HBA configuration was reloaded, and a new management connection then returned `1`. The driver text and collector fields are separate evidence channels; libpq/psycopg did not expose the startup SQLSTATE in this run.

## Diagnosis {#diagnosis}

Record the user, database, connection source, authentication method, server version, and the first matching HBA rule without recording passwords. Correlate the failed attempt with a verbose collector record. The collector detail can identify the HBA line, while the client startup exception may contain only a FATAL text string.

Check that the intended role exists and can log in, that its password was set for the selected method, and that no earlier HBA rule intercepts the connection. A correct password under the wrong HBA method is not proof of a correct deployment. Avoid treating a password rotation as complete until a new connection succeeds.

There is no transaction state to recover for the rejected session. Keep a controlled administration connection while changing HBA rules, reload the configuration, and test with a fresh client. Restore the prior rule exactly when the test is complete and verify that a new owner connection still works.

## Response and repair {#response}

- Use the intended secret and authentication method for the role, and rotate it through a channel that does not expose the value in logs or command history.
- Inspect the first matching `pg_hba.conf` rule, correct its database, user, address, and method fields, then reload it.
- Test the affected role with a new connection and a real harmless query such as `SELECT 1`.
- Keep existing administration access until the new path is proven, then close stale pool connections so they do not retain old credentials.

The representative repair includes both a known-password connection and a fresh post-restore management connection. A successful `pg_reload_conf()` alone is not an authentication proof, and reusing the connection that performed the reload does not test the repaired login path.

## Versions and boundaries {#versions}

The catalogue contains `28P01` in the first scanned definition (`9.0.0` or earlier), every listed formal snapshot through PostgreSQL 18.6, and the PostgreSQL 19 Beta 3 preview. No condition definition change is recorded in the scanned range; the pre-9.0 introduction point remains outside the scan.

The wrong-password case passed on PostgreSQL 18.6 and 10.21. The source line differs between releases, and the driver/collector SQLSTATE split is a property of this startup path. Certificate, GSSAPI, PAM, LDAP, peer, and HBA syntax failures are separate authentication paths and are not covered by this evidence.

## Related {#related}

[`53300` — `too_many_connections`](../53300/) is a capacity failure during connection startup. [`57014` — `query_canceled`](../57014/) occurs while a connected backend is executing a statement. [`42501` — `insufficient_privilege`](../42501/) is a privilege check after authentication, not a password diagnostic.

## Sources {#sources}

Structured evidence is recorded in the [public evidence JSON](../data/evidence/28p01.json). Source records are fixed to PostgreSQL commit `724edf9bde9d356724ad384a2e196edc3c9f80f7`; runtime records retain the collector output, driver observation, HBA restoration, both target IDs, and structured observations.

- `src.errcodes.18.6` — [`errcodes.txt`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/errcodes.txt#L271-L278)
- `src.auth.18.6` — [`auth.c`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/libpq/auth.c#L270-L332)
- `doc.client-auth.18` — [The pg_hba.conf File](https://www.postgresql.org/docs/18/auth-pg-hba-conf.html) and [Password Authentication](https://www.postgresql.org/docs/18/auth-password.html)
- `doc.protocol.18` — [Error and Notice Message Fields](https://www.postgresql.org/docs/18/protocol-error-fields.html)
- Runtime: `28P01-registry-final-20260909` on latest and pg10; structured observations are in the [public evidence JSON](../data/evidence/28p01.json)
