# 28000 — 授权规范无效（invalid_authorization_specification）

> PostgreSQL SQLSTATE 28000（授权规范无效，invalid_authorization_specification）的源码证据、诊断与处理参考。
---

# 28000 — 授权规范无效

## 速览 {#at-a-glance}

SQLSTATE `28000` 是 Class `28` 中的 **invalid_authorization_specification**。`28000` 是被拒绝的启动或授权上下文所属的类别。选定的角色不存在启动路径在建立会话前发送 C=`28000`、S=`FATAL` 以及 `role "<generated>" does not exist`；固定认证路径还在证书、pg_hba 和 LOGIN 资格失败时使用本类的具体报文。

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

| 字段 | 值 |
| --- | --- |
| SQLSTATE | `28000` |
| 条件名 | `invalid_authorization_specification` |
| 状态 | `有效` |
| 已知存在于 | `7.4` |
| 锁定快照 | `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` |
| 宏 | `ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION` |
| 别名 | `—` |

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

## 含义 {#meaning}

本类覆盖建立或认证连接时被拒绝的授权上下文。选定分支是启动阶段的角色查找：服务器无法为请求角色创建会话，于是发送 FATAL ErrorResponse。证书、pg_hba 和 LOGIN 资格路径仍是同一类别下的不同 producer。

选定的角色查找发生在后端进入普通会话之前。这解释了 `FATAL` 级别以及失败尝试没有会话级事务。不要把它与 28P01（密码认证失败）、42501（会话建立后的权限不足）或 3D000（数据库选择）混在一起；应由启动阶段和服务器字段确定分支。

### 启动分支指引 {#scenarios}

| 检查内容 | 选定的角色缺失分支 | Class 28 的其他可能性 |
| --- | --- | --- |
| 服务器字段 | `C=28000`、`S=FATAL`、`M=role "%s" does not exist` | 保留服务器实际返回的代码和主报文，不能只按类别推断。 |
| 会话状态 | 失败尝试没有可用会话，也没有事务 | 后续授权检查可能发生在已建立会话中，恢复边界不同。 |
| 修复 | 按意图创建/重命名角色，或修正启动用户，然后重新连接 | 根据实际诊断修正证书、`pg_hba.conf`、LOGIN 属性或映射。 |

## 诊断 {#diagnosis}

以服务器 ErrorResponse 或认证日志作为 SQLSTATE 依据，并先判断阶段：角色查找、pg_hba 规则、证书，还是 LOGIN 权限。选定的 `collector` 是原始服务器 ErrorResponse 记录，不是 csvlog/jsonlog；原始启动尝试与 psycopg 尝试彼此独立。选定运行中 psycopg 报告的驱动 SQLSTATE 为 `null`，失败连接没有事务；两个独立的新鲜已知角色探针仍能执行 `SELECT 1`，这不是日志采集器关联结论。

对于角色不存在，使用独立管理会话对照请求的启动用户和角色目录，并检查引号或大小写折叠。保留原始 `C`、`S`、`M` 字段：psycopg 的 `null` 只说明它自己的失败启动尝试，不表示服务器没有 SQLSTATE。新鲜探针必须是另一条连接，不能把失败尝试变成可回滚的事务。

## 处理 {#response}

按服务器指出的原因修正角色、LOGIN/映射、证书或 pg_hba 规则，再建立新连接。失败启动连接上没有可供 `ROLLBACK` 的会话；没有服务器字段时不能仅凭客户端异常认定 28000，也不要盲目重放非幂等启动工作。

角色或认证配置改变后，使用准确的目标用户和数据库重新连接，并在新会话中确认身份。如果应用已经在另一条连接上发送了工作，应单独核对那部分工作；启动前的 `FATAL` 本身没有可重试的业务事务。

## 实测诊断 {#messages}

选定的启动角色分支以 `FATAL` 发出主报文 `role "%s" does not exist`，角色名是动态值。Class 28 的其他授权失败可能使用不同主报文。由于客户端没有得到可用会话 SQLSTATE，应以服务器 ErrorResponse 为准。

选定服务器记录为 `role "u28000_missing_fbc912bb7e6f" does not exist`，角色名是本次运行生成的值。原始 ErrorResponse 与 psycopg 的 `null` 属于两次独立启动尝试，不能拼成同一会话的两种视图。

## 代表案例 {#case}

<!-- BEGIN SQLSTATE SNIPPET: invalid_authorization -->

注册表包含服务器启动 ErrorResponse 后执行的探针。触发点是在新启动连接上使用随机角色，失败发生在会话建立前，不能用 SQL 语句重现。

```sql
SELECT 1;
```

<!-- END SQLSTATE SNIPPET -->

18.6 服务器 ErrorResponse 为 C=`28000`、S=`FATAL`、`role "u28000_missing_fbc912bb7e6f" does not exist`。驱动启动诊断的 SQLSTATE 为 `null`，失败连接没有事务；已知可用连接探针返回 `1`，状态 `IDLE`。

可下载的案例与证据投影分别是 [`28000 案例 JSON`](../../data/cases/28000.json) 和 [`作者证据`](../../data/evidence/28000.json)。运行器清单为 `verify/cases/28000/cases.json`；发布前会将页面 SQL 与共享注册表比对。

## 版本 {#versions}

上面的生成事实表记录锁定的目录快照和最早观察到的定义。本页自然运行范围是 PostgreSQL 18.6 与 10.21，不能据此推断所有中间版本的行为。

## 相关 {#related}

- [`0P000` — invalid_role_specification](../0p000/)
- [`28P01` — 相关条件](../28p01/)
- [`3D000` — invalid_catalog_name](../3d000/)

## 来源 {#sources}

- `src.miscinit-missing-role.18.6` — `src/backend/utils/init/miscinit.c` at `REL_18_6` commit `724edf9bde9d356724ad384a2e196edc3c9f80f7`; fixed blob SHA-256 `54bdb859c143a6c70d86067ccf2e124f59aa0852e5705bb653652e41a60af39a` ([source](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/init/miscinit.c#L802-L804)).
- `src.miscinit-missing-role.10.23` — `src/backend/utils/init/miscinit.c` at `REL_10_23` commit `02991e79f8f58bc208f05dcc8af0c62dbe0a6ea4`; fixed blob SHA-256 `c4d2e234f96644d43aad9e9a6728df693e7f75175f05a550701413ceee70fb82` ([source](https://github.com/postgres/postgres/blob/02991e79f8f58bc208f05dcc8af0c62dbe0a6ea4/src/backend/utils/init/miscinit.c#L516-L518)).
- `src.calls.REL_18_6` / `src.calls.REL_10_23` — fixed local call scans, SHA-256 `9ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf` / `00d16d3eb01b71ccf1b245c8f3102f9d0ec9f36fb02777b8dd1b99fcb263040c`; these scans preserve the resolved call context used by the claims.
