跳到主要内容

常见错误速查

CloudBase PostgreSQL 数据库使用过程中出错时,多半涉及 GRANT / RLS Policy / JWT 三层。本文按「错误现象 → 根因 → 排查」的格式列出常见问题,方便快速定位。

💡 阅读建议:先从「快速排查决策树」入手定位大类问题,再翻具体小节。

快速排查决策树

请求失败 / 拿不到数据

├─ HTTP 401 ? → JWT 缺失 / 过期 / 签名错误 → 见 [§ JWT 与认证](#jwt)
├─ HTTP 403 ? → 表级 GRANT 不足 → 见 [§ 表级 GRANT](#grant)
│ → 或 RLS WITH CHECK 拒绝写入
├─ HTTP 404 ? → 表 / RPC 函数不存在 → 见 [§ 资源不存在](#not-found)
├─ HTTP 406 ? → 单对象返回不符合数量约束 → 见 [§ 返回格式](#return-format)
├─ HTTP 409 ? → 唯一约束 / 外键冲突 → 检查表约束
├─ HTTP 500 ? → 函数内 RAISE / 实例错误 → 见 [§ 内部错误](#internal)
└─ 200 但数据空 ? → RLS 静默过滤 → 见 [§ RLS 静默过滤](#silent)

JWT 与认证

401 Unauthorized / JWT expired

现象根因排查
完全未带 Authorization Header客户端忘记携带前端确保使用 SDK(自动携带)或手动添加 Authorization: Bearer <token>
JWT expiredaccess_token 过期前端调用刷新或重新登录;Publishable Key / API Key 永不过期
Invalid JWT签名错误、Token 被篡改检查是否复制粘贴时混入空格、换行;确认环境 ID 与 Token 中 aud / project_id 匹配
iss claim mismatch跨环境使用了别的环境的 Token前端环境 ID 必须与 Token 签发环境一致

检查当前 Token 中的 role / sub

最快的方法是用 jwt.io 或后端解码(不验签):

echo "<your-jwt>" | awk -F. '{print $2}' | base64 -d 2>/dev/null

应能看到 role: "anon" | "authenticated" | "service_role"sub: "<uuid>"

表级 GRANT

permission denied for table xxx

ERROR: permission denied for table todos
根因排查
角色没有对应 DML 的 GRANT\dp public.todos 检查;按需 GRANT SELECT/INSERT/UPDATE/DELETE ON public.todos TO <role>;
RLS Policy 已写但漏掉 GRANTRLS 是第二层,GRANT 是第一层。两层都要过
用了非业务角色应用流量必须落到 anon / authenticated / service_role 三者之一,其他系统角色不可用

permission denied for sequence xxx_id_seq

ERROR: permission denied for sequence todos_id_seq

原因serial / bigserial 主键背后是 SEQUENCE 对象,授予表的 INSERT 不会自动授予 sequence 权限。

修复

GRANT USAGE, SELECT ON SEQUENCE public.todos_id_seq TO authenticated;
GRANT USAGE, SELECT ON SEQUENCE public.todos_id_seq TO service_role;

一个表的 sequence 名通常是 <table>_<column>_seq,可用 \ds public.* 查看。

RLS Policy

new row violates row-level security policy for table "xxx"

ERROR: new row violates row-level security policy for table "orders"
根因排查
WITH CHECK 不通过写入的某字段不满足 Policy。最常见:前端伪造 owner_id/buyer_id 与 JWT sub 不一致 → 让数据库 DEFAULT 自动绑定,前端别传
INSERT Policy 缺失启用 RLS 后必须为每种角色明确写 INSERT Policy(或角色具备 BYPASSRLS)
UPDATE Policy 没写 WITH CHECK只写 USING 时,UPDATE 后行不会被检查;建议 USING + WITH CHECK 配套

200 OK 但 SELECT 返回空数组(RLS 静默过滤)

特征:HTTP 200,无报错,但 data[]null

根因排查
当前角色(如 anon)没有匹配的 SELECT Policy即使加 ?owner_id=eq.xxx 过滤也无济于事,先得通过 RLS
Policy 条件没匹配上当前用户ExecutePGSql 指定 Role: authenticated 模拟执行,看是不是预期
current_setting('request.jwt.claims', true) 为空检查请求是否真的带了 JWT、网关是否成功解析(一般直接 select current_setting(...) 在 SQL 编辑器里反而看不到,是 PostgREST 才会注入)

调试技巧:用 ExecutePGSql 模拟特定角色

{
"EnvId": "<envId>",
"Sql": "SELECT * FROM public.todos",
"Role": "authenticated"
}

不用真的发 HTTP 请求即可验证 Policy 是否生效。

资源不存在

404 Not Found 调用 RPC 时

根因排查
函数名拼错与 SQL 中 CREATE FUNCTION 名称完全一致(含 schema)
函数不在 public schema 下且未配置 db-schemas默认仅 public 下的函数会被 PostgREST 暴露
函数刚创建,PostgREST metadata 未刷新等几秒重试,或尝试通知后端刷新
参数签名不匹配PostgREST 通过参数名 + 类型寻址;JSON body 中所有 key 必须与函数 argname 完全相同

404 Not Found 访问表时

{"message":"relation \"public.todoes\" does not exist"}
  • 表名、schema 拼错
  • 表不在 public schema 中(需要 Accept-Profile: <schema> Header 切换)

返回格式

406 Not Acceptable JSON object requested, multiple (or no) rows returned

{"message":"JSON object requested, multiple (or no) rows returned"}

原因:你设置了 Accept: application/vnd.pgrst.object+json,但结果不是恰好 1 行。

修复:要么去掉该 Header,要么用 .single() / .maybeSingle() 并保证查询条件能精确匹配 1 行。

写入后想拿到完整行

Prefer: return=representation

curl -X POST '.../v1/rdb/rest/orders' \
-H 'Authorization: Bearer <token>' \
-H 'Prefer: return=representation' \
-H 'Content-Type: application/json' \
-d '{"product_id":1, ...}'

ExecutePGSql 错误

InternalError 执行 DDL 失败

部分 DDL(CREATE/ALTER/DROP/GRANT/REVOKE/TRUNCATE/COMMENT 等)直接执行可能返回 InternalError

修复:用 DO $$ BEGIN EXECUTE '...'; END $$ 包装重试:

DO $$ BEGIN EXECUTE 'CREATE TABLE public.products (id serial PRIMARY KEY, name text)'; END $$;

字符串内的单引号 ' 需转义为 ''

每次 API 调用只能执行一条 SQL

ExecutePGSql 不支持多语句拼接,请按 ; 拆分逐条调用。

permission denied(带 Role 参数时)

带了 "Role": "authenticated" 参数后,被以普通角色执行;触发了 GRANT/RLS。这不是 bug——这正是它的用途。如需绕过权限调试,去掉 Role 参数。

内部错误

500 Internal Server Error 来自函数 RAISE

{"message":"商品不存在或库存不足"}

这是函数体内 RAISE EXCEPTION '...' 抛出的业务错误,正常的失败链路。前端按业务文案处理即可。

实例不可用 / 503

数据库实例临时故障或重启。客户端做指数退避重试即可。

网关错误

INVALID_REQUEST / PERMISSION_DENIED / RESOURCE_NOT_FOUND

参考 HTTP API - PostgREST RESTful API 的错误码表,与 PostgreSQL 原生错误的对应关系:

网关错误码HTTP含义
INVALID_PARAM400请求参数无效
INVALID_REQUEST400 / 406请求体无效 / 单对象数量约束不满足
PERMISSION_DENIED401 / 403鉴权失败 / 权限不足
RESOURCE_NOT_FOUND404表 / 函数不存在
SYS_ERR500系统内部错误
OPERATION_FAILED503数据库连接失败
RESOURCE_UNAVAILABLE503数据库不可用

通用排查清单

遇到数据库相关问题,按下面顺序自查 80% 都能定位:

  1. JWT 是否带了? Authorization: Bearer <token> 不能漏
  2. JWT 中 role 是不是预期的? 用 base64 解 payload 看一眼
  3. 表级 GRANT 是否给了对应角色? \dp <table> 检查
  4. RLS 是否启用? SELECT relname, relrowsecurity FROM pg_class WHERE relname='xxx';
  5. 该角色 + 该操作有没有匹配的 Policy? \d+ <table> 看 Policy 列表
  6. Policy 的 USING / WITH CHECK 是否真的对当前用户成立?ExecutePGSqlRole 模拟执行
  7. serial 主键忘记授 SEQUENCE 权限了吗?

下一步