supabase-common-errors
Diagnose and fix Supabase errors across PostgREST, PostgreSQL, Auth, Storage, and Realtime. Use when encountering error codes like PGRST301, 42501, 23505, or auth failures. Use when debugging failed queries, RLS policy violations, or HTTP 4xx/5xx responses. Trigger with "supabase error", "fix supabase", "PGRST", "supabase 403", "RLS not working", "supabase auth error", "unique constraint", "foreign key violation".
Allowed Tools
Provided by Plugin
supabase-pack
Claude Code skill pack for Supabase (30 skills)
Installation
This skill is included in the supabase-pack plugin:
/plugin install supabase-pack@claude-code-plugins-plus
Click to copy
Instructions
Supabase Common Errors
Overview
Diagnostic guide for Supabase errors across PostgREST (PGRST*), PostgreSQL (numeric codes), Auth, Storage, and Realtime. Identify the error layer, trace the root cause, and apply the correct fix — database queries return { data, error }; other SDK services have their own result, callback, and exception contracts.
The workflow is three steps: capture the error object, classify it by layer and code, then apply and verify the fix. Full step-by-step code lives in the diagnostic walkthrough; complete lookup tables are in the error reference.
Prerequisites
@supabase/supabase-jsinstalled (npm install @supabase/supabase-js)SUPABASE_URLand a publishable/anon key configured; normal request clients use verified user identity and RLS- Access to Supabase Dashboard (for log inspection and SQL Editor)
- Supabase CLI installed for local development (
npx supabase --version)
Instructions
Step 1 — Capture the Error Object
Supabase database queries return a { data, error } result. Never assume data exists — always destructure and check error first, because data is null whenever error is set.
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`);
Do not diagnose the installed SDK version solely from an undefined property. Check which service/method produced the result, its documented shape, and the locked dependency version. Realtime subscriptions and Python clients differ.
Step 2 — Identify the Error Layer and Code
Match the code prefix to its subsystem, then look it up in the Error Handling tables below:
PGRST*→ PostgREST (API gateway: JWT, query parsing, schema)- 5-character SQLSTATE (e.g.
42501,23505) → PostgreSQL engine (RLS, constraints, migrations) AuthApiError→ Auth service (credentials, confirmation, token expiry)StorageApiError→ Storage service (bucket, RLS onstorage.objects, size limits)
A missing code needs service-specific inspection: 401 can reflect invalid user credentials or a missing bearer identity, and 500 has multiple possible causes. Status alone does not prove a wrong project key or failing SQL function. A paste-ready diagnoseSupabaseError() classifier is in the walkthrough.
Step 3 — Apply the Fix and Verify
Apply the fix from the matching Error Handling table, then re-run the original operation to confirm. Common recoveries:
- For
PGRST301, verify the JWT/project/session configuration; it means the JWT cannot be decoded or is invalid. Use the Auth session recovery workflow only when appropriate, not an automatic retry loop. - For
42501, inspect verified user identity, grants, and RLS under that identity. It means insufficient privilege, not necessarily RLS. Do not bypass the user boundary with a service-role client in the request path. - After a migration, reload the PostgREST schema cache (an authorized SQL session using
NOTIFY pgrst, 'reload schema').
Full before/after fix code for both cases is in the walkthrough.
Output
Deliverables after applying this skill:
- Error identified by code and layer (PostgREST, PostgreSQL, Auth, Storage, Realtime)
- Root cause isolated using the diagnostic helper or manual code inspection
- Fix applied from the Error Handling table and verified against the original failing operation
- Guard code in place (
if (error)checks) preventing silent null-data bugs
Error Handling
The two most-cited layers are inline below. Auth, Storage, and Realtime tables are in the full error reference.
PostgREST API Errors (PGRST\*)
| Code | HTTP | Meaning | Root Cause | Fix |
|---|---|---|---|---|
PGRST301 |
401 | JWT cannot be decoded or is invalid | Invalid token or mismatched configuration | Verify token/project/session through the appropriate Auth workflow; do not retry blindly |
PGRST302 |
401 | Bearer request absent while anonymous role disabled | Request lacks required identity | Verify authenticated bearer propagation and server configuration |
PGRST116 |
406 | Singular result has zero or multiple rows | Result does not satisfy exactly-one contract | Use .maybeSingle() for zero-or-one; correct multiple matches |
PGRST200 |
400 | Relationship/schema-cache mismatch | Embedded relationship absent or stale | Check foreign keys and embedding; refresh cache if appropriate |
PGRST204 |
400 | Column not found | Column name doesn't exist in the table or view | Verify column exists with supabase gen types typescript; check for typos |
PGRST000 |
503 | Database connection unavailable | Invalid database URI or database not running | Inspect service configuration and availability |
PGRST003 |
504 | Pool acquisition timed out | No pool connection became available | Inspect query load and pool capacity; apply bounded admission |
PostgreSQL Database Errors (SQLSTATE)
| Code | Meaning | Root Cause | Fix |
|---|---|---|---|
42501 |
Insufficient privilege | Grants or RLS deny this identity | Review grants and policies with the actual authorized user |
23505 |
Unique constraint violation | INSERT/UPDATE conflicts with an existing row | Review conflict semantics; .upsert(row, { onConflict: 'column' }) can overwrite data and needs authorization |
23503 |
Foreign key violation | Referenced row doesn't exist in the parent table | Insert the parent row first, or check the foreign key value |
42P01 |
Table or relation doesn't exist | Migration not applied, or wrong schema | Run supabase db push; verify schema with \dt in SQL Editor |
42703 |
Column doesn't exist | Schema out of sync with code | Regenerate types: supabase gen types typescript --local > types/supabase.ts |
57014 |
Query cancelled (statement timeout) | Query took longer than statement_timeout |
Add indexes; simplify the query; increase timeout in postgresql.conf |
Examples
The most common failure — calling .single() on optional data — is inline below. Three more worked examples (upsert to dodge 23505, Realtime subscription error handling, and serverless pool exhaustion) are in the examples reference.
Handling .single() on optional data (PGRST116)
const { data: profile, error } = await supabase
.from('profiles')
.select('id, display_name')
.eq('user_id', userId)
.maybeSingle();
if (error) throw error;
if (!profile) {
// A successful zero-row read is not authorization to create an arbitrary profile.
// Present the application's separately authorized create-profile workflow.
}
Resources
- Full diagnostic walkthrough — every capture/classify/fix code block
- Complete error reference — Auth, Storage, and Realtime tables
- Worked examples — upsert, Realtime, serverless pooling
- Supabase JavaScript SDK Reference
- PostgREST Error Codes
- PostgreSQL Error Codes
- RLS Debugging Guide
- Supabase Realtime Troubleshooting
- Supabase Status Page
Next Steps
- Use
supabase-debug-bundleto generate a full diagnostic snapshot when errors persist after applying these fixes. - Use
supabase-security-basicsto audit your RLS policies and prevent42501errors proactively. - Use
supabase-known-pitfallsfor edge cases and SDK behavior that can cause subtle bugs. - Use
supabase-observabilityto set up logging and alerting so you catch errors before users report them.