Debugging PGRST Error Codes in Supabase
Codoric Team

Debugging PGRST Error Codes in Supabase
Supabase's auto-generated REST API is powered by PostgREST underneath, and when something goes wrong at that layer, the error response includes a PGRST-prefixed code rather than a generic HTTP status alone. These codes are specific and searchable, but the messages attached to them are terser than they need to be for someone hitting them for the first time. Here are the ones that come up most often in real projects.
PGRST116: "JSON object requested, multiple (or no) rows returned"
When it happens: you called .single() on a query that returned either zero rows or more than one row. .single() specifically expects exactly one row and errors otherwise — it's not a generic "get the first row" helper, and it's easy to reach for it assuming that's what it does.
// Throws PGRST116 if no project with this id exists, or if id isn't unique
const { data, error } = await supabase
.from('projects')
.select('*')
.eq('id', projectId)
.single();
The fix depends on which case you're actually hitting:
- If zero rows is a valid, expected outcome (the record might legitimately not exist), use
.maybeSingle()instead, which returnsnullfordatawithout erroring:
const { data, error } = await supabase
.from('projects')
.select('*')
.eq('id', projectId)
.maybeSingle();
- If more than one row is coming back unexpectedly, the filter isn't as unique as assumed — check whether the column you're filtering on actually has a uniqueness constraint, or whether the query needs an additional filter to disambiguate.
PGRST200: "Could not find a relationship between X and Y in the schema cache"
When it happens: you're using Supabase's embedded resource syntax to join tables in a single query, and PostgREST can't find a foreign key relationship to base the join on:
// Errors if there's no FK from comments to posts (or PostgREST hasn't seen it yet)
const { data } = await supabase
.from('posts')
.select('*, comments(*)');
Two distinct causes, and they need different fixes:
- The foreign key genuinely doesn't exist. PostgREST infers embeddable relationships directly from foreign key constraints — if
comments.post_idisn't declared as a foreign key referencingposts.id, there's no relationship for it to find, even if the column values line up logically. Add the constraint:
alter table public.comments
add constraint comments_post_id_fkey
foreign key (post_id) references public.posts(id);
- The foreign key exists, but PostgREST's schema cache is stale. PostgREST caches the schema for performance and doesn't automatically know about a constraint added moments ago through a migration, especially right after a deploy. Reload the schema cache — in the Supabase dashboard this can be triggered from the API settings, or via
notify pgrst, 'reload schema';run directly against the database.
If there are multiple foreign keys between the same two tables (a common case: a messages table with both a sender_id and recipient_id referencing users), PostgREST can't tell which relationship you mean and needs to be told explicitly using the constraint name or an explicit hint:
const { data } = await supabase
.from('messages')
.select('*, sender:users!messages_sender_id_fkey(*)');
PGRST301: JWT-related errors
Errors in this range come from PostgREST's own JWT validation — distinct from Supabase Auth's client-side session handling covered in a previous troubleshooting post. If you're seeing a PGRST-prefixed code specifically (rather than the plainer "JWT expired" message from the client library), it means the request reached PostgREST with a token it couldn't validate — check that the Authorization header is actually being attached to the request, and that the anon/service key configured on the client matches the project the token was issued for. A mismatched Supabase URL/key pair (easy to introduce when copying .env values between a staging and production project) produces exactly this class of error, and it's worth checking first before assuming the token itself is the problem.
General debugging approach for any PGRST error
- Read the code, not just the message. The numeric code is more specific and more searchable than the human-readable message, which is sometimes generic.
- Reproduce the exact query directly against Postgres (via the SQL editor or
psql) to rule out whether the issue is the underlying data/schema versus something specific to how PostgREST is interpreting the request. - Check the schema cache is current whenever the error involves relationships or columns that were part of a very recent migration — this single step resolves a surprising fraction of "it should work" reports.
- Check for multiple valid relationships or ambiguous filters before assuming a bug in PostgREST itself — most PGRST200 and PGRST116 errors trace back to the data model being less unique or less connected than the query assumed, not to PostgREST misbehaving.
These errors are usually fast to resolve once the specific code is identified — the time sink is almost always in not knowing which of the above four categories a given code falls into.
If you're stuck on a specific PGRST error in a real project and want a fast second opinion, book a call — most of these are a quick diagnosis with visibility into the actual schema and query.