Fix guides / Supabase: "infinite recursion detected in policy"

How to fix Supabase "infinite recursion detected in policy for relation"

Your app suddenly loads nothing and the console shows infinite recursion detected in policy for relation "profiles" (code 42P17). This is very common in Lovable and Bolt apps that add admin roles or team membership.

Why it happens

A row-level security policy on a table runs a query against that same table. For example, a policy on profiles that checks (select role from profiles where id = auth.uid()) = 'admin'. To answer that subquery Postgres must apply the policy again, which runs the subquery again, forever. Postgres stops and throws 42P17. The same happens when two tables have policies that query each other (for example teams and team_members).

Fix: move the lookup into a security definer function

A function marked security definer runs as its owner and skips RLS, so it breaks the loop. Run this in the SQL editor, adjusted to your table names:

create or replace function public.is_admin()
returns boolean
language sql
security definer
set search_path = public
stable
as $$
  select exists (
    select 1 from public.profiles
    where id = auth.uid() and role = 'admin'
  );
$$;

drop policy if exists "Admins can read all profiles" on public.profiles;
create policy "Admins can read all profiles"
on public.profiles for select
to authenticated
using ( id = auth.uid() or public.is_admin() );

For team membership, make a function like is_team_member(team_id uuid) the same way and call it from both tables' policies.

Better long term: keep roles out of the protected table

Store roles in a separate table such as user_roles, with its own simple policy (user_id = auth.uid()), or in the user's app_metadata which you can read in policies with auth.jwt(). Never put roles in user_metadata, because users can edit it themselves.

Check the fix

  1. Run select * from pg_policies where tablename = 'profiles'; and make sure no policy still selects from its own table.
  2. Log in as a normal user and as an admin and load the page that failed.
  3. Confirm a normal user still cannot see other users' rows.
Still stuck? No shame in it: these bugs hide in places your AI builder cannot see. Send me the error and your repo and I will fix it as a pull request within 24 hours, explained in plain English, for $49. Several things broken? The $149 Full Rescue covers up to 3 issues plus a security pass, within 72 hours. Not fixed means full refund.
Quick Fix, $49Full Rescue, $149Ask first

Last updated 2026-10-21.