Supabase Error: Fix PGRST116 "JSON object requested, multiple (or no) rows returned"

11. August 2026 approx. 4 min read Supabase
Contents 10

Your query looks correct – yet the Supabase JS client throws:

JSON object requested, multiple (or no) rows returned
code: PGRST116

This almost always comes from .single() (or .maybeSingle() misuse) when PostgREST did not get exactly one row. Here is how to fix it cleanly.

The Problem: .single() Means Exactly One Row

.single() tells PostgREST: return one JSON object. If the result set is empty or has more than one row, you get PGRST116 instead of data.

Common causes:

  • No matching row (filter too strict, wrong id, RLS hid the row)
  • Multiple rows match (missing unique filter)
  • Using .single() on a list endpoint by accident

Empty results under RLS often feel like “the row is missing” – double-check policies and auth.uid(). For insert-time RLS errors see new row violates row-level security policy.

The Solution: Pick the Right Expectation

Step 1: Use maybeSingle When Zero Rows Are OK

const { data, error } = await supabase
  .from('profiles')
  .select('id, display_name')
  .eq('id', userId)
  .maybeSingle()

if (error) {
  console.error(error.code, error.message)
  return
}

if (!data) {
  // no profile yet – create one or show onboarding
}

maybeSingle() returns null for zero rows and still errors if multiple rows match.

Step 2: Keep .single() Only for Guaranteed Unique Rows

// OK when id is primary key and the row must exist
const { data, error } = await supabase
  .from('profiles')
  .select('*')
  .eq('id', session.user.id)
  .single()

If the row might not exist yet, prefer maybeSingle() or handle PGRST116 explicitly.

Step 3: Fix Filters When Multiple Rows Match

// Too loose → multiple rows → PGRST116
await supabase.from('orders').select('*').eq('user_id', userId).single()

// Better: unique key or limit + explicit choice
await supabase
  .from('orders')
  .select('*')
  .eq('id', orderId)
  .eq('user_id', userId)
  .maybeSingle()

Step 4: Separate “No Row” From Real Errors

const { data, error } = await supabase
  .from('profiles')
  .select('*')
  .eq('id', userId)
  .maybeSingle()

if (error) throw error
if (!data) {
  await supabase.from('profiles').insert({ id: userId })
}

Common Mistakes

  • Treating PGRST116 as a network failure: it is a result-shape mismatch.
  • Assuming the row is missing when RLS blocked SELECT: grants/policies can hide rows – see also permission denied for table (42501).
  • Chaining .single() after inserts without SELECT policy: related to the classic RLS insert failure on return.

Troubleshooting Checklist

  1. ✅ Is zero rows a valid outcome? → maybeSingle()
  2. ✅ Filter unique enough (primary/unique key)?
  3. ✅ RLS SELECT policy allows the row for this user?
  4. ✅ Not accidentally querying a one-to-many relation with .single()?

Conclusion

PGRST116 means PostgREST did not get exactly one row for a singular response. Use maybeSingle() when absence is normal, tighten filters when multiples appear, and verify RLS if “missing” rows should be visible.

Still seeing PGRST116? Share the query shape (table + filters, no secrets) in a comment.

Comments