Supabase RLS Debugger

Your RLS policy looks correct.
It isn't.

Connect your Supabase project. Find out which policies are silently blocking your users — before they do.

No credit card  ·  Read-only access  ·  Results in 60 seconds

rlslab — scan results
$ rlslab scan --project my-saas Scanning 7 tables across 3 calling contexts... ❌ CRITICAL table: documents policy: "Users can view their documents" USING: auth.uid() = owner_id Failure: auth.uid() returns NULL in Next.js App Router context Server client initialized without session. All rows denied. Fix: Use createServerClient() with cookie forwarding ⚠ WARNING table: organization_members USING: organization_id IN (SELECT organization_id FROM organization_members WHERE user_id = auth.uid()) Failure: Self-referencing subquery → infinite recursion → HTTP 500 Fix: Wrap in SECURITY DEFINER function to break recursion cycle ⚠ WARNING table: invoices policy: "Users can update their invoices" FOR: UPDATE USING (auth.uid() = user_id) Failure: Missing WITH CHECK — UI returns success, row not persisted Fix: Add WITH CHECK (auth.uid() = user_id) to UPDATE policy ✓ OK tables: profiles, teams, team_members, audit_logs ───────────────────────────────────────────── 1 critical 2 warnings 4 passed · 7 tables scanned

You wrote the policy. You tested it in the SQL Editor. It passed.

Then you shipped — and your users couldn't see their data.

The SQL Editor runs as postgres. It bypasses every RLS policy you wrote. What you tested is not what your users hit.

"I spent 2 days debugging what turned out to be auth.uid() returning NULL in my server-side queries. My policy was fine. My context was wrong."

r/Supabase

"Ran prisma migrate deploy and my entire API stopped working. Every table returned 0 rows. Took a full day to find out Prisma had silently revoked my schema grants."

GitHub Discussions

"Enabled RLS on my campaigns table and my Realtime subscriptions stopped firing. The policy was completely permissive. Didn't matter."

r/Supabase

"My UPDATE policy looked right. The UI said success. The row never changed. Turns out I had a USING clause where I needed WITH CHECK. Postgres just... said nothing."

r/Supabase, 29 upvotes

These are the four most common failure modes in Supabase RLS. Each reported hundreds of times. Each invisible to every existing tool.

What's actually happening
when your queries break

The Supabase SQL Editor bypasses all of these. It runs as postgres. You test. It passes. You ship. It breaks.

What you see What's actually happening
CRIT Empty results after SSR auth auth.uid() returns NULL server-side. Session not forwarded. All rows silently denied.
CRIT HTTP 500 on org-scoped SELECT Your policy references the same table it's protecting. Infinite recursion. Postgres panics.
CRIT Everything breaks after prisma migrate Prisma revoked GRANT on your schema. RLS didn't cause this — but the error is identical. You'll debug the wrong thing for hours.
WARN UPDATE returns success, row unchanged You wrote USING where Postgres needed WITH CHECK. Silent failure. No error thrown. UI lies to your user.
WARN Realtime subscriptions go silent RLS evaluation at the broadcast path differs from PostgREST. Enabling any policy breaks subscriptions regardless of permissiveness.
WARN 42501 error — cause unknown Could be RLS denial. Could be a missing GRANT. The error is identical. No tool tells you which one. You guess for hours.
CRIT Clerk users blocked everywhere Clerk doesn't write to auth.users. auth.uid() is permanently NULL. Every RLS tutorial you followed is wrong for Clerk users.
WARN Queries 10–100× slower at scale Your membership-check policy re-evaluates on every row. That's O(N×M). Invisible at 100 rows. Fatal at 100,000.

No existing tool detects any of the 8 failures above — not the Security Advisor, not SupaExplorer, not the AI prompt, not ZenStack.

Not "your policy may have an issue."
The exact failure. The exact fix.

Context-aware. Uses your actual schema, your actual column names, your actual auth provider.

policy scan ❌ CRITICAL
CREATE POLICY "Users can view their documents" ON documents FOR SELECT USING (auth.uid() = owner_id); -- ^ Correct SQL. Wrong context.
Failure: auth.uid() returns NULL in Next.js App Router.
Server client initialized without user session.
Impact: All server-side queries return 0 rows. No error.
policy scan ⚠ WARNING
CREATE POLICY "Users can update invoices" ON invoices FOR UPDATE USING (auth.uid() = user_id); -- Missing WITH CHECK clause. -- Postgres silently applies USING as WITH CHECK. -- If updated row no longer matches → silent failure.
Failure: UPDATE returns 200. Row not saved. Zero error logged.
Fix: Add WITH CHECK (auth.uid() = user_id) to this policy.
auto-fix output ✓ FIXED
CREATE POLICY "Users can update invoices" ON invoices FOR UPDATE USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id); -- USING: filter rows user can see before update -- WITH CHECK: validate row after update is applied
Fix applied. UPDATE now correctly validates the row after modification.

Same stack. No migration.
Results in 60 seconds.

Connect

OAuth to your Supabase project. Read-only. We don't touch your data, your policies, or your schema.

Scan

We run your policies against simulated authenticated, SSR, service role, and anonymous contexts — and show you exactly what each returns and why.

See the failure

Not "your policy may have an issue." The exact failure mode, exact line, exact reason — in plain language, not a Postgres error code.

Get the fix

Context-aware SQL using your actual column names and auth provider — not a template. Includes an explanation of what was wrong and why the fix works.

Start free. Pay when it saves you time.

One prevented 2-day debugging incident = 5 months of subscription.

Diagnose

Free
 

Connect your project. See exactly which policies are broken and why. No card required.

  • Scan up to 3 tables
  • Detect 5 critical failure modes
  • NULL context detection (SSR, Edge Fn)
  • USING / WITH CHECK validation
  • Missing GRANT detection
  • Recursion risk flagging
Start Free Scan

Trust

$79/mo
or $599 one-time

CI-enforced confidence. Auto-generated tests + GitHub Action that fails your build if a migration breaks a policy.

If a migration passes our CI check and breaks a policy in production, we fix the test suite and refund the month.
  • Everything in Fix
  • Auto-generated pgTAP test suite
  • GitHub Action CI integration
  • Policy regression alerts on deploy
  • Performance audit (O(N×M) detection)
  • Realtime policy validator
  • One additional team seat
Get Trust

Audit — one-time

$149

Submit your project. Receive a complete policy set + pgTAP tests + a Loom walkthrough of every failure found — within 24 hours. Outcome guaranteed: if delivered policies fail in production for a covered failure mode, full re-audit free.

Book Audit →

Answers

Why does the SQL Editor say my policy is fine?
The SQL Editor runs queries as the postgres superuser, which bypasses Row Level Security entirely. You are never testing your actual policy when you use the SQL Editor — you are testing a superuser query with RLS disabled. This is true regardless of which user you appear to be logged in as in the Studio UI.
Will this change anything in my project?
No. We connect read-only via OAuth. We simulate query contexts against your schema structure — we never modify your policies, data, or configuration. You can verify this by watching the network tab during the scan: no write requests are made to your project.
I'm using Clerk instead of Supabase Auth. Does this work?
Yes. Clerk + Supabase is a specific failure class we handle explicitly. The core issue — auth.uid() returns NULL because Clerk users don't exist in auth.users — is one of our top detected patterns. We generate the correct current_setting('request.jwt.claims')::json->>'sub' pattern for every policy that needs it.
My schema is complex. Will the fix suggestions actually work?
We use your actual schema — column names, foreign keys, role structure — not generic templates. If the automated suggestion doesn't resolve your case, the 48-hour manual guarantee kicks in: a human debugs your specific failure and delivers the corrected policy. If we can't fix it within 48 hours, your next month is free.
How is this different from the Supabase Security Advisor?
The Security Advisor tells you which tables are missing RLS. It has no way to tell you why an existing policy is silently failing at runtime. Those are different problems. Most builders hit the second one — after they've already written their policies.
What if I already have 15 tables with RLS?
That's the exact use case this was built for. Connect the project, run a full scan, and you'll see which of your 15 tables have risky or broken policies in one view — sorted by severity. No need to review each table manually.
Can't I just ask ChatGPT to fix my RLS?
You probably already tried this. The AI gave you SQL that looked correct — because it is syntactically correct. But it fails at runtime because AI doesn't know your calling context. It doesn't know you're using the anon client in a server component, that your Prisma migration already wiped your grants, or that your Clerk users aren't in auth.users. AI without context generates the right-looking wrong answer.