Cannot coerce the result to a single JSON object
A Supabase query asked for exactly one row with single(), but its filter matched no rows or more than one.
Also appears as
- {"code":"PGRST116","details":"The result contains 0 rows","hint":null,"message":"Cannot coerce the result to a single JSON object"}
- JSON object requested, multiple (or no) rows returned
Prompt to fix it
A Supabase query fails with "Cannot coerce the result to a single JSON object" (PGRST116). Find the query that ends in .single() and check the error’s details to see whether it got 0 rows or several. For 0 rows, check whether the row exists and whether a row-level security policy hides it from this user. If no row is a normal case, switch to .maybeSingle() and handle null in the UI. For several rows, filter on a unique column. Keep RLS on, and don’t add .limit(1) just to hide duplicates.
Paste into Layerwise or any AI coding tool.
What it means
Ending a supabase-js query with .single() tells Supabase’s API to return one object instead of a list. If the query matches anything other than exactly one row, the API refuses with code PGRST116 and status 406, and details gives the count, like The result contains 0 rows. Projects on older API versions, and .maybeSingle() when it finds several rows, word it “JSON object requested, multiple (or no) rows returned.”
Zero rows is the common case, and row-level security (RLS) is often why. A row hidden by RLS looks exactly like a row that doesn’t exist, so a profile that’s really there can still come back as 0 rows. An update(...).eq(...).select().single() behaves the same way: if no UPDATE policy matches, nothing changes and nothing comes back.
Use .maybeSingle() when “not found” is a normal answer, such as a profile that hasn’t been created yet. It returns null for zero rows. When several rows match, tighten the filter to a unique column such as id.
Common causes
- The row doesn’t exist yet, such as the profile of someone who just signed up.
- Row-level security hides the row from the current user, so the query sees 0 rows.
- The filter isn’t unique, like
.eq('email', email)on a table with repeated emails, so it matches several rows. - An
updatefollowed by.select().single()matched no rows, because of its filter or a missingUPDATEpolicy.
How to fix it
- Read
detailsin the error to see whether the query found 0 rows or several. - For 0 rows, check in the SQL editor that the row exists, then check the table’s
SELECTpolicies (andUPDATEpolicies for updates) for the signed-in user. - If “no row” is a valid result, switch to
.maybeSingle()and handlenullin the UI. - For several rows, filter on a unique column such as
id, or add a unique constraint so duplicates can’t be created.
Example fix
// Zero rows is a normal answer here: the profile may not exist yet.
const { data: profile, error } = await supabase
.from('profiles')
.select('*')
.eq('id', user.id)
.maybeSingle();
if (error) throw error;
if (!profile) {
// Show onboarding, or create the profile.
}Related errors
All errorsNew row violates row-level security policy
Postgres refused to save a row because no row-level security policy on that table lets the current user write it.
Cannot read properties of undefined
The code asked for a property of a value that doesn’t exist yet, usually data that hasn’t loaded or a field the API didn’t return.
Duplicate key value violates unique constraint
The app tried to save a row whose ID, email, or other unique value is already in the table, so Postgres rejected the copy.
Relation does not exist
The query names a table that Postgres can’t find in this database, usually because it was never created here or has a different name.
Build with an agent that checks its work
The Layerwise agent clicks through the live preview and reads the console, so it catches errors before you do.