SupabaseDatabaseAuth

Supabase: infinite recursion detected in policy

infinite recursion detected in policy for relation "profiles"

In short: An RLS policy on a table queries that same table (or a view that does), so Postgres loops until it stops the query.

Why it happens

Row Level Security policies run as part of every query on the table. If a policy's USING or WITH CHECK clause reads from the same table, that read re-enters the policy, which reads again, and Postgres raises 42P17. A common AI-generated pattern is a profiles policy that looks up the current user's team_id with select team_id from profiles where id = auth.uid(). Policies on views, or helper functions that are not SECURITY DEFINER, can trigger the same loop. Wrapping auth.uid() in a subselect is a performance tip, not a fix for recursion.

How to fix it

  1. Find the policy named in the error (Database → Policies, or pg_policies for that table) and look for a subquery or function that reads the same table.
  2. Move the self-lookup into a small SECURITY DEFINER function that only returns the current user's own row (for example their team_id), with set search_path = public and stable. Because it bypasses RLS, keep the function tight and grant execute only to authenticated.
  3. Change the policy to call that function instead of querying the table directly.
  4. Or avoid the loop another way: store the needed value on a different table (like members) whose policies don't read back from the first table.
  5. Re-run the query as a signed-in user, not in the Table Editor, which bypasses RLS.

Example

create or replace function public.my_team_id()
returns uuid
language sql
stable
security definer
set search_path = public
as $$
  select team_id from public.profiles where id = auth.uid()
$$;

revoke all on function public.my_team_id() from public;
grant execute on function public.my_team_id() to authenticated;

create policy "Users read teammates"
on public.profiles for select to authenticated
using ( team_id = public.my_team_id() );
Don't: Don't disable RLS, and don't make a SECURITY DEFINER function that returns other people's rows or accepts an arbitrary user id.

Copy-paste fix prompt

3 · The fix promptPaste into Cursor, Claude Code, Codex, Lovable, Bolt, Replit or v0
I'm getting this error in my app:

infinite recursion detected in policy for relation "profiles"

[Paste the full error, stack trace, and the file it points to here]

Likely cause: An RLS policy on a table queries that same table (or a view that does), so Postgres loops until it stops the query.

Task: Find the RLS policy on the table in the error that queries the same table (or a non-definer function that does). Replace that self-lookup with a tight SECURITY DEFINER helper or a non-recursive table. Don't disable RLS.

Rules:
- First explain the root cause in 2 sentences and list the files you'll change.
- Make the smallest change that fixes it. Don't refactor unrelated code.
- Don't disable security or checks (RLS, auth, signature verification, type checking, CORS allow-all) to make the error disappear.
- Never print or hard-code secret keys.
- Finish by telling me exactly how to verify the fix.

Common with: Lovable, Bolt, Cursor, v0. Last reviewed 2026-11-15.

Related fixes

← All fixes · Still stuck? Use the diagnose-first prompt