↑↓ select↵ open⌫ change scopeOpen full search

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

Wiki / SQLSTATE

28P01 — invalid_password

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

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
Invalid Authorization Specification
condition name
invalid_password
lang
en
sqlstate
28P01

Definition PG 18

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.

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.

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 {#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 is a capacity failure during connection startup. 57014 — query_canceled occurs while a connected backend is executing a statement. 42501 — insufficient_privilege is a privilege check after authentication, not a password diagnostic.

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

Compare versions

SQLSTATE evidence and recorded runtime versions

cases · wrong_password_authentication
{
  "assertions": [
    "The connection attempt receives SQLSTATE 28P01",
    "The server does not open a transaction for the rejected connection",
    "The known password opens a new session while the temporary rule is active",
    "A fresh management connection succeeds after the original configuration is restored"
  ],
  "case_id": "wrong_password_authentication",
  "cleanup": "Restore pg_hba.conf and drop the role and schema.",
  "has_snippet": true,
  "position": 0,
  "preconditions": [
    "A disposable login role with a known password",
    "A temporary first pg_hba.conf rule uses md5 and is reloaded"
  ],
  "repair": "Use the intended secret and authentication method, verify a new connection, then restore or rotate credentials without logging passwords.",
  "trigger": "Connect with a wrong password and retain the server's authentication diagnostic.",
  "versions": [
    "10",
    "18"
  ]
}
claims · identity.class-and-condition
{
  "claim_id": "identity.class-and-condition",
  "limits": "Directory identity does not identify the selected authentication method or the matched HBA rule.",
  "method": "Read the Class 28 section and 28P01 row in the frozen errcodes.txt snapshot.",
  "position": 0,
  "runtime": [],
  "sources": [
    "src.errcodes.18.6"
  ],
  "statement": "28P01 is the invalid_password condition in Class 28 invalid_authorization_specification."
}
claims · mechanism.auth-failure
{
  "claim_id": "mechanism.auth-failure",
  "limits": "Other authentication methods and HBA configuration errors use separate branches and can report different SQLSTATEs or messages.",
  "method": "Trace auth_failed's password/MD5/SCRAM error selection and detail construction and compare the official HBA and password-authentication rules.",
  "position": 1,
  "runtime": [],
  "sources": [
    "src.auth.18.6",
    "doc.client-auth.18"
  ],
  "statement": "The password authentication failure path in auth.c selects ERRCODE_INVALID_PASSWORD, reports password authentication failed for user \"%s\" at FATAL, and may add the matched pg_hba.conf line to log detail."
}
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 driver diagnostics and collector SQL state code, severity, message, HBA detail, and source location in both final summaries and raw records.",
  "position": 2,
  "runtime": [],
  "sources": [
    "doc.protocol.18",
    "runtime.28P01-registry-final-20260909.latest",
    "runtime.28P01-registry-final-20260909.pg10"
  ],
  "statement": "For the rejected startup connection, psycopg exposed startup text with sqlstate null while the PostgreSQL collector recorded FATAL SQLSTATE 28P01 and the password-failure message on both targets."
}
claims · runtime.authentication-repair
{
  "claim_id": "runtime.authentication-repair",
  "limits": "The run proves a password failure under the controlled md5 rule; it does not cover certificate, GSSAPI, PAM, LDAP, peer, or HBA syntax failures.",
  "method": "Use an owner connection to set the disposable role password, reload the temporary HBA rule, test wrong and known passwords through new connections, restore and reload the original rule, and assert a fresh owner probe.",
  "position": 3,
  "runtime": [],
  "sources": [
    "case-manifest.28P01",
    "snippet-registry.28P01.final",
    "runtime.28P01-registry-final-20260909.latest",
    "runtime.28P01-registry-final-20260909.pg10"
  ],
  "statement": "After enabling the temporary md5 rule, the known password opened a new role connection and returned SELECT 1 = 1; after exact HBA restoration, a fresh management connection also returned 1 on both targets."
}
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": 4,
  "runtime": [],
  "sources": [
    "manifest.28P01"
  ],
  "statement": "The locked catalogue records 28P01 in every listed formal snapshot from 9.0.23 through 18.6 and in 19beta3; pre-9.0 history is not scanned."
}
messages · message.password-failure
{
  "limits": "The fixed PG18.6 source passes logdetail through errdetail_log; a client ErrorResponse need not expose it. PG10 collector output uses a release-specific pg_hba.conf wording and may include an additional password-mismatch line.",
  "message_id": "message.password-failure",
  "path": "",
  "position": 0,
  "raw": {
    "detail_template": "Connection matched file \"%s\" line %d: \"%s\"",
    "detail_type": "collector log detail assembled at runtime",
    "id": "message.password-failure",
    "limits": "The fixed PG18.6 source passes logdetail through errdetail_log; a client ErrorResponse need not expose it. PG10 collector output uses a release-specific pg_hba.conf wording and may include an additional password-mismatch line.",
    "primary_template": "password authentication failed for user \"%s\"",
    "severity_source": "explicit FATAL",
    "sources": [
      "src.auth.18.6"
    ],
    "sqlstate": "28P01"
  },
  "severity": "explicit FATAL",
  "sources": [
    "src.auth.18.6"
  ],
  "templates": [
    {
      "kind": "primary",
      "literal": "password authentication failed for user \" \"",
      "position": 0,
      "role": "",
      "template": "password authentication failed for user \"%s\""
    },
    {
      "kind": "detail",
      "literal": "Connection matched file \" \" line : \" \"",
      "position": 1,
      "role": "",
      "template": "Connection matched file \"%s\" line %d: \"%s\""
    }
  ]
}
runtimes · runtime.28P01-registry-final-20260909.latest · 18.6 (Homebrew) · passed
{
  "cases": [
    "wrong_password_authentication"
  ],
  "limits": "",
  "position": 0,
  "raw": {
    "cases": [
      "wrong_password_authentication"
    ],
    "id": "runtime.28P01-registry-final-20260909.latest",
    "observed": {
      "collector_detail": "Connection matched file \"\u003cpg_hba.conf path\u003e\" line 1: \"host all all 0.0.0.0/0 md5\"",
      "collector_message": "password authentication failed for user \u003cgenerated-role\u003e",
      "collector_severity": "FATAL",
      "collector_source": "auth.c:320",
      "collector_sqlstate": "28P01",
      "driver_sqlstate": null,
      "driver_text": "FATAL: password authentication failed for user \u003cgenerated-role\u003e",
      "hba_reload": {
        "enabled": true,
        "restored": true
      },
      "known_password_probe": 1,
      "restored_management_probe": 1,
      "transaction": "none opened for rejected startup"
    },
    "raw": "verify/results/28P01-registry-final-20260909/latest/raw.jsonl",
    "raw_sha256": "97bcf605a806511aff3f0f4eeaa3615c159bf54e7d6f35b7bfbb45940d3d86d2",
    "run_id": "28P01-registry-final-20260909",
    "server_version": "18.6 (Homebrew)",
    "server_version_num": 180006,
    "snippet_registry": {
      "path": "verify/cases/28P01/snippets.json",
      "sha256": "9b2ca3d14cb093415043cb1cb8291eee2020df052f8067c4952ce2cd4597d395"
    },
    "status": "passed",
    "summary": "verify/results/28P01-registry-final-20260909/latest/summary.json",
    "summary_sha256": "0f58bf277fdd33be8d39ed8f05dc6de5555b8aa89db344b1a4cdfb3eb425bcbf",
    "target": "latest"
  },
  "run_id": "28P01-registry-final-20260909",
  "runtime_id": "runtime.28P01-registry-final-20260909.latest",
  "server_version": "18.6 (Homebrew)",
  "status": "passed",
  "target": "latest"
}
runtimes · runtime.28P01-registry-final-20260909.pg10 · 10.21 (Debian 10.21-1.pgdg90+1) · passed
{
  "cases": [
    "wrong_password_authentication"
  ],
  "limits": "",
  "position": 1,
  "raw": {
    "cases": [
      "wrong_password_authentication"
    ],
    "id": "runtime.28P01-registry-final-20260909.pg10",
    "image": "postgres@sha256:b2baf8998630663d21370da06387c950e587071bdd307ee34e661cdcc7442bcc",
    "observed": {
      "collector_detail": "Password does not match for user \u003cgenerated-role\u003e.\\nConnection matched pg_hba.conf line 1: \"host all all 0.0.0.0/0 md5\"",
      "collector_message": "password authentication failed for user \u003cgenerated-role\u003e",
      "collector_severity": "FATAL",
      "collector_source": "auth.c:329",
      "collector_sqlstate": "28P01",
      "driver_sqlstate": null,
      "driver_text": "FATAL: password authentication failed for user \u003cgenerated-role\u003e",
      "hba_reload": {
        "enabled": true,
        "restored": true
      },
      "known_password_probe": 1,
      "restored_management_probe": 1,
      "transaction": "none opened for rejected startup"
    },
    "raw": "verify/results/28P01-registry-final-20260909/pg10/raw.jsonl",
    "raw_sha256": "6d67921dfd66d347593d1b5c348cb60402eee0aa8dfc2cb9c2dbba7872caefb8",
    "run_id": "28P01-registry-final-20260909",
    "server_version": "10.21 (Debian 10.21-1.pgdg90+1)",
    "server_version_num": 100021,
    "snippet_registry": {
      "path": "verify/cases/28P01/snippets.json",
      "sha256": "9b2ca3d14cb093415043cb1cb8291eee2020df052f8067c4952ce2cd4597d395"
    },
    "status": "passed",
    "summary": "verify/results/28P01-registry-final-20260909/pg10/summary.json",
    "summary_sha256": "c8b82b3c42519fe121d27a815f224a25a12b8d14fdffd111dedd23f7a8a0a334",
    "target": "pg10"
  },
  "run_id": "28P01-registry-final-20260909",
  "runtime_id": "runtime.28P01-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 271-278",
  "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.auth.18.6
{
  "commit": "724edf9bde9d356724ad384a2e196edc3c9f80f7",
  "docs_url": "",
  "kind": "",
  "location": "lines 270-332",
  "path": "src/backend/libpq/auth.c",
  "position": 1,
  "sha256": "94252cb1e2c49b0ddb15f6596d0abf8056c84493de07cc81439da4c5b07018f1",
  "source_id": "src.auth.18.6",
  "tag": "REL_18_6",
  "url": "https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/libpq/auth.c#L270-L332"
}
sources · doc.client-auth.18
{
  "commit": "724edf9bde9d356724ad384a2e196edc3c9f80f7",
  "docs_url": "",
  "kind": "",
  "location": "pg_hba.conf and password authentication",
  "path": "doc/src/sgml/client-auth.sgml",
  "position": 2,
  "sha256": "3fb31769e212f5ff97ffca769fe4c72c5963b90d0165bd6147f089e310167326",
  "source_id": "doc.client-auth.18",
  "tag": "PG18-docs",
  "url": "https://www.postgresql.org/docs/18/auth-pg-hba-conf.html"
}
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.28P01
{
  "commit": "",
  "docs_url": "",
  "kind": "local_artifact",
  "location": "snapshots and definition_blobs entries for the 28P01 definition",
  "path": "sources/manifest.lock.json",
  "position": 4,
  "sha256": "1727a275f336988ff96b4f9990a4ca253080f73fc5d316e7def165c8cf3708a8",
  "source_id": "manifest.28P01",
  "tag": "",
  "url": "../sources/manifest.lock.json"
}
sources · case-manifest.28P01
{
  "commit": "",
  "docs_url": "",
  "kind": "local_artifact",
  "location": "wrong_password_authentication",
  "path": "verify/cases/28P01/cases.json",
  "position": 5,
  "sha256": "f6b0cfcd0752036c7d3368586fb86740b5ef9364da20800554be02e89d44149f",
  "source_id": "case-manifest.28P01",
  "tag": "",
  "url": "../verify/cases/28P01/cases.json"
}
sources · snippet-registry.28P01.final
{
  "commit": "",
  "docs_url": "",
  "kind": "local_artifact",
  "location": "wrong_password_authentication ordered SQL",
  "path": "verify/cases/28P01/snippets.json",
  "position": 6,
  "sha256": "9b2ca3d14cb093415043cb1cb8291eee2020df052f8067c4952ce2cd4597d395",
  "source_id": "snippet-registry.28P01.final",
  "tag": "",
  "url": "../verify/cases/28P01/snippets.json"
}

Source references

Full definition and evidence JSON

Definition sources

center · PostgreSQL 18 · english-manuals:890476e06c67c07baa2c0404c5accab30fc55ff21e0e5f0cf28d7af62891e504

Text language: en · english-manuals:890476e06c67c07baa2c0404c5accab30fc55ff21e0e5f0cf28d7af62891e504