Skip to content

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

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.

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

Meaning and trigger paths

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

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.

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;

The latest target’s collector record was:

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

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

  • 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

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.

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

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, HBA restoration, both target IDs, and structured observations.