Short answer
If the row may not exist yet, such as a profile for a brand-new user, use .maybeSingle(), which accepts zero or one row. If the row should exist, check that the user is signed in and that a row level security policy lets them read it. If two rows match, filter on a unique column.
Why .single() fails, and the fix
01
The row doesn’t exist yet
“Query result must be zero or one row” Supabase, JavaScript: maybeSingle
.single() expects exactly one row. When the row is optional, such as settings or a profile that is created later, switch to .maybeSingle(), which accepts zero or one row, and handle the empty case in the app. If more than one row matches, it still returns an error.
const { data, error } = await supabase .from('characters') .select() .eq('name', 'Katniss') .maybeSingle()02
Row level security hides the row
“Think of a policy as adding a WHERE clause to every query.” Supabase, Row Level Security
The row is in the table, but no policy lets this user read it, so the query sees zero rows. Check the select policy on the table in the Supabase dashboard, and test it as the same user the app signs in as.
03
The user isn’t signed in, or the session expired
“When a request is made without an authenticated user (e.g., no access token is provided or the session has expired), auth.uid() returns null” Supabase, Row Level Security
A policy that compares auth.uid() with a user_id column matches nothing when there is no valid session, so a query that works while signed in fails afterwards. Make sure the query runs after the session loads, and send the user to sign in when it is missing.
04
More than one row matches
“Query result must be one row” Supabase, JavaScript: single
If the filter is on a column that isn’t unique, such as an email stored twice or a user_id with duplicate rows, .single() fails the same way. Filter on the primary key, or clean up the duplicates and add a unique constraint so it can’t happen again.
Checked against the vendor’s docs on : Supabase docs: PostgREST error codes; Supabase docs: single; Supabase docs: maybeSingle; Supabase docs: Row Level Security.
Rather hand it over?
Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.
- from $450App Fix TicketOne reproducible bug in an existing mobile or web app, diagnosed and fixed.See scope and start
- from $450AI-Built App RescueA Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way.See scope and start
Related: Supabase select returns an empty array · Supabase new row violates RLS · Supabase OAuth provider disabled · Supabase Invalid login credentials