概述
Supabase Common Errors 是一个针对 Supabase 错误的分层诊断指南,覆盖 PostgREST(PGRST*)、PostgreSQL(数字错误码)、Auth、Storage 和 Realtime 五个子系统。目标是帮助定位错误所在层级、追溯根因并应用正确修复方式。关键前提:SDK 的每次调用都会返回 { data, error } 元组,当 error 存在时 data 为 null。
工作流分三步:捕获错误对象、按层级和错误码分类、应用并验证修复。完整的逐步代码位于诊断演练中,完整速查表位于错误参考中。
先决条件
- 已安装 @supabase/supabase-js(npm install @supabase/supabase-js)
- 已配置 SUPABASE_URL 和 SUPABASE_ANON_KEY(或 SUPABASE_SERVICE_ROLE_KEY)
- 可访问 Supabase Dashboard(用于查看日志和 SQL Editor)
- 本地开发需安装 Supabase CLI(npx supabase --version)
使用方式
第一步 — 捕获错误对象
不要假定 data 一定存在,应先解构并检查 error。当 error 存在时 data 为 null。
const { data, error } = await supabase.from('todos').select('*')
if (error) {
console.error(`[${error.code}] ${error.message}`)
return // data is null here — do not touch it
}
console.log(`Found ${data.length} rows`)
如果 error 是 undefined 而不是 null,请升级到 @supabase/supabase-js@2.x。
第二步 — 识别错误层级与错误码
按错误码前缀匹配子系统:
- PGRST* → PostgREST(API 网关:JWT、查询解析、schema)
- 5 位数字(如 42501、23505)→ PostgreSQL 引擎(RLS、约束、迁移)
- AuthApiError → Auth 服务(凭证、确认、令牌过期)
- StorageApiError → Storage 服务(bucket、storage.objects 上的 RLS、大小限制)
缺少错误码时通常看 HTTP 状态:401 → SUPABASE_ANON_KEY 错误或缺失;500 → 数据库函数中未处理的异常。
第三步 — 应用修复并验证
应用对应速查表中的修复后,重新执行原操作确认。常见恢复手段:
- 用 supabase.auth.refreshSession() 刷新过期的 JWT(PGRST301)。
- 用 service-role 客户端重新查询以确认 RLS 拦截(42501),再修正策略。
- 迁移后重载 PostgREST schema 缓存(Dashboard → Settings → API → "Reload schema cache",或 NOTIFY pgrst, 'reload schema')。
输出
应用本技能后的交付物:
- 按错误码和层级识别的错误(PostgREST、PostgreSQL、Auth、Storage、Realtime)
- 通过诊断辅助工具或人工代码检查定位根因
- 已从错误处理表应用修复,并针对原失败操作验证
- 已加入守卫代码(if (error) 检查),防止静默的 null-data 缺陷
错误处理速查
PostgREST API 错误(PGRST*)
| 错误码 | HTTP | 含义 | 根因 | 修复 |
|---|---|---|---|---|
| PGRST301 | 401 | JWT 过期或无效 | SUPABASE_ANON_KEY 错误,或用户会话过期 | 验证 SUPABASE_ANON_KEY 与项目匹配;调用 supabase.auth.refreshSession() |
| PGRST302 | 401 | 缺少 Authorization 头 | 客户端创建时未带 key,或中间件剥离了头 | 向 createClient() 传入 SUPABASE_ANON_KEY;检查代理/CDN 配置 |
| PGRST116 | 406 | .single() 未返回行 | 查询匹配 0 行但 .single() 期望恰好 1 行 | 可选查询用 .maybeSingle(),或检查过滤条件 |
| PGRST200 | 400 | 无效查询参数 | 过滤格式错误、运算符错误或列引用无效 | 检查过滤语法:.eq('col', val) 而非 .eq('col = val') |
| PGRST204 | 400 | 列不存在 | 表或视图中不存在该列名 | 用 supabase gen types typescript 验证列是否存在;检查拼写 |
| PGRST000 | 503 | 连接池耗尽 | 来自 serverless 函数的并发连接过多 | 在项目设置中启用 pgBouncer(Supavisor);减少连接数 |
PostgreSQL 数据库错误(5 位数字码)
| 错误码 | 含义 | 根因 | 修复 |
|---|---|---|---|
| 42501 | RLS 策略违规 | 行级安全策略阻止了该用户的操作 | 添加或修正 RLS 策略;用 service role 测试确认 |
| 23505 | 唯一约束冲突 | INSERT/UPDATE 与已有行冲突 | 使用 .upsert({ onConflict: 'column' }) 或先检查存在性 |
| 23503 | 外键违规 | 父表中不存在被引用的行 | 先插入父行,或检查外键值 |
| 42P01 | 表或关系不存在 | 迁移未应用,或 schema 错误 | 运行 supabase db push;在 SQL Editor 中用 \dt 验证 schema |
| 42703 | 列不存在 | schema 与代码不同步 | 重新生成类型:supabase gen types typescript --local > types/supabase.ts |
| 57014 | 查询被取消(语句超时) | 查询耗时超过 statement_timeout | 添加索引;简化查询;在 postgresql.conf 中增大超时 |
示例
对可选数据调用 .single()(PGRST116)
// BAD — throws PGRST116 when the user has no profile row
const { data: profile } = await supabase
.from('profiles').select('*').eq('user_id', userId).single()
// GOOD — returns null instead of erroring
const { data: profile, error } = await supabase
.from('profiles').select('*').eq('user_id', userId).maybeSingle()
if (!profile) {
await supabase.from('profiles').insert({ user_id: userId, display_name: 'New User' })
}
更多示例(用 upsert 规避 23505、Realtime 订阅错误处理、serverless 连接池耗尽)位于示例参考中。
适用场景
- 开发中遇到 Supabase 返回的错误码需要快速定位层级与根因
- 排查 RLS、约束冲突、迁移未应用等数据库层问题
- 处理 JWT 过期、schema 缓存未刷新等 API 层问题
- 为代码加入 error 守卫,避免静默 null-data 缺陷
限制
- 资料仅覆盖 PostgREST、PostgreSQL、Auth、Storage、Realtime 的错误处理;完整 Auth、Storage、Realtime 错误表未在本文展开,需参考完整错误参考。
- 未提供官方文档原文,部分链接与延伸资源(诊断演练、错误参考、示例参考、SDK 文档、状态页等)仅以资料中名称列出。
- Auth、Storage、Realtime 的具体错误码与修复步骤暂未提供。
后续步骤
- 若修复后错误仍存在,使用 supabase-debug-bundle 生成完整诊断快照。
- 使用 supabase-security-basics 审计 RLS 策略,主动预防 42501 错误。
- 使用 supabase-known-pitfalls 了解可能导致隐蔽缺陷的边界情况和 SDK 行为。
- 使用 supabase-observability 设置日志和告警,在用户报告前捕获错误。