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

11. August 2026 ca. 4 Min. Lesezeit Supabase
Inhalt 10

Deine Query sieht richtig aus – und trotzdem wirft der Supabase-JS-Client:

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

Das kommt fast immer von .single() (oder falsch genutztem .maybeSingle()), wenn PostgREST nicht genau eine Zeile bekommt. So behebst du es sauber.

Das Problem: .single() heißt genau eine Zeile

.single() sagt PostgREST: gib ein JSON-Objekt zurück. Ist das Ergebnis leer oder hat mehr als eine Zeile, kommt PGRST116 statt Daten.

Häufige Ursachen:

  • Keine passende Zeile (Filter zu streng, falsche ID, RLS hat die Zeile versteckt)
  • Mehrere Zeilen passen (kein eindeutiger Filter)
  • .single() versehentlich auf einer Listen-Abfrage

Leere Ergebnisse unter RLS fühlen sich oft wie „Zeile fehlt“ an – Policies und auth.uid() prüfen. Bei Insert-RLS-Fehlern: new row violates row-level security policy.

Die Lösung: Erwartung klar wählen

Schritt 1: maybeSingle, wenn 0 Zeilen ok sind

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) {
  // noch kein Profil – anlegen oder Onboarding zeigen
}

maybeSingle() liefert null bei 0 Zeilen und fehlerhaft weiter, wenn mehrere Zeilen passen.

Schritt 2: .single() nur bei garantiert eindeutigen Zeilen

// OK, wenn id Primary Key ist und die Zeile existieren muss
const { data, error } = await supabase
  .from('profiles')
  .select('*')
  .eq('id', session.user.id)
  .single()

Wenn die Zeile noch fehlen darf: lieber maybeSingle() oder PGRST116 bewusst behandeln.

Schritt 3: Filter schärfen bei mehreren Treffern

// Zu lose → mehrere Zeilen → PGRST116
await supabase.from('orders').select('*').eq('user_id', userId).single()

// Besser: eindeutiger Key
await supabase
  .from('orders')
  .select('*')
  .eq('id', orderId)
  .eq('user_id', userId)
  .maybeSingle()

Schritt 4: „Keine Zeile“ von echten Fehlern trennen

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 })
}

Häufige Fehler

  • PGRST116 als Netzwerkfehler behandeln: es ist ein Result-Shape-Mismatch.
  • Annehmen die Zeile fehlt, obwohl RLS SELECT blockiert: siehe auch permission denied for table.
  • .single() nach Insert ohne SELECT-Policy: verwandt mit dem klassischen RLS-Insert-Fehler beim Return.

Checkliste

  1. ✅ Sind 0 Zeilen ein gültiges Ergebnis? → maybeSingle()
  2. ✅ Filter eindeutig genug (Primary/Unique Key)?
  3. ✅ RLS SELECT-Policy erlaubt die Zeile für diesen User?
  4. ✅ Kein versehentliches .single() auf 1:n-Relationen?

Fazit

PGRST116 heißt: PostgREST hat für eine Singular-Antwort nicht genau eine Zeile bekommen. maybeSingle() wenn Fehlen normal ist, Filter schärfen bei Mehrfachtreffern, RLS prüfen wenn „fehlende“ Zeilen sichtbar sein sollten.

Verwandte Artikel

Siehst du weiter PGRST116? Query-Form (Tabelle + Filter, ohne Secrets) in einen Kommentar.

Kommentare