Deine RLS-Policies sehen gut aus – und trotzdem wirft der Supabase-Client:
permission denied for table projects
ERROR: 42501
Oder PostgREST meldet 42501 / „permission denied“. Das ist nicht dasselbe wie new row violates row-level security policy. Hier erkennst du den Unterschied und setzt die Grants richtig.
Das Problem: GRANT kommt vor RLS
Postgres prüft Rechte in Schichten. Tabellen-GRANTs für die Rolle deines Requests (anon oder authenticated) laufen vor Row Level Security. Fehlt der Privilege auf der Tabelle, stoppt Postgres mit 42501 – deine Policies werden gar nicht ausgewertet.
Typische Auslöser:
- Tabelle per SQL / Prisma / Drizzle angelegt, ohne Grants für
authenticated/anon - Falscher Key: Aufruf mit Anon-Key, obwohl nur
authenticatedRechte hat - Schema/Tabelle in der API sichtbar, aber nach Migration fehlen Privileges
Siehst du dagegen new row violates row-level security policy, bist du schon an den Grants vorbei – dann Policies fixen (oder returning: 'minimal'). Siehe den RLS-Insert-Fehler-Guide.
Die Lösung: API-Rollen explizit berechtigen
Schritt 1: Fehlercode bestätigen
const { data, error } = await supabase.from('projects').select('*')
if (error) {
console.error(error.code, error.message)
// 42501 / "permission denied for table …" → Grants
// "new row violates row-level security policy" → RLS-Policies
}
Schritt 2: Aktuelle Grants prüfen
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_schema = 'public'
AND table_name = 'projects'
ORDER BY grantee, privilege_type;
Du brauchst die Rolle, die zu deinem Client-Aufruf passt (authenticated für eingeloggte User, anon nur wenn anonymer Zugriff gewollt ist).
Schritt 3: Least Privilege gewähren
-- Eingeloggte User über Anon/Publishable-Key + User-JWT
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE public.projects TO authenticated;
-- Nur wenn anonyme Clients lesen/schreiben sollen (Writes eher selten)
-- GRANT SELECT ON TABLE public.projects TO anon;
-- Sequenzen für serial / identity Spalten
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO authenticated;
Danach RLS eingeschaltet lassen. Grants öffnen die Tür; Policies entscheiden, welche Zeilen durchkommen.
Schritt 4: API-Schema neu laden (falls nötig)
Nach neuen Tabellen oder Grant-Änderungen braucht PostgREST oft einen Schema-Reload (Dashboard → Settings → API → Reload, oder Cache-Wartezeit). Ein fehlender Grant kann auch wie „table not found in schema cache“ aussehen.
Häufige Fehler
- Mehr RLS-Policies gegen 42501 schreiben: Policies laufen erst, wenn Grants existieren.
- Nur sich selbst / postgres berechtigen: SQL-Editor funktioniert; der JS-Client scheitert weiter.
- Breite Grants an anon ohne RLS: Sicherheitsloch – lieber
authenticated+ enge Policies. - Verwechslung mit Storage 403: Storage nutzt Policies auf
storage.objects– siehe Storage 403 RLS beim Upload.
Checkliste
- ✅ Fehler ist
42501/permission denied for table(nicht RLS violate)? - ✅ Client nutzt den richtigen Key + Session?
- ✅
role_table_grantszeigt Rechte fürauthenticated(oderanon)? - ✅ RLS nach dem Grant weiterhin aktiv mit echten Policies?
- ✅ API-Schema neu geladen, wenn die Tabelle brandneu ist?
Fazit
permission denied for table ist ein fehlender GRANT, keine kaputte RLS-Policy. API-Rolle berechtigen, RLS anlassen, danach Policies feinjustieren. Für die andere Klassiker-Meldung – Insert durch RLS blockiert – nimm den Row-Level-Security-Policy-Fix.
Verwandte Artikel
- RLS beheben: new row violates row-level security policy
- infinite recursion detected in policy beheben
- Supabase Storage 403 RLS beim Upload
- Supabase Auth: Invalid login credentials
- Supabase Edge Functions: Invalid JWT / 401
- Supabase RLS Dokumentation
Hängst du weiter bei 42501? Schreib Tabellenname und ob du als anon oder authenticated aufrufst in einen Kommentar – ohne Secrets.
Kommentare