Common errors · Supabase
Supabase: infinite recursion detected in policy for relation
Cause A policy on a table queries that same table (directly or through another one), so Postgres evaluates the policy again and loops.
Fix Move the membership lookup into a SECURITY DEFINER function with a pinned search_path and call it from the policies.
Have it fixed for you, 290 EURSee the fix step by step
Typical of team or organisation apps generated by Lovable or Bolt: a policy on members checks membership by selecting from members, which triggers the same policy again.
Where it breaks
Which case is yours?
- A policy on table A queries table A (directly or through table B whose policy queries A). Postgres evaluates the policy for the subquery too and loops.
The fix, step by step
- Move the membership lookup into a SECURITY DEFINER function. It runs with the owner's rights, so RLS is not evaluated again inside it.
create or replace function public.is_member(org uuid) returns boolean language sql security definer stable set search_path = public as $$ select exists ( select 1 from members where org_id = org and user_id = auth.uid() ); $$; revoke execute on function public.is_member(uuid) from anon; - Rewrite the policies to call the function instead of the subquery.
drop policy if exists "members can read" on members; create policy "members can read" on members for select to authenticated using (public.is_member(org_id)); - Keep the function in a schema the API does not expose if it returns anything sensitive, and always pin search_path as above.
How to check it is fixed
Run the app as a member and as a non-member: the member sees the team, the non-member gets zero rows, and the recursion error is gone from the Postgres logs.
Delivery example for this error
- using (org_id in (select ...))+ using (public.is_member(org_id))+ -- security definer function- Root cause: A policy on a table queries that same table (directly or through another one), so Postgres evaluates the policy again and loops.
- Change made: Move the membership lookup into a SECURITY DEFINER function with a pinned search_path and call it from the policies.
- Run the app as a member and as a non-member: the member sees the team, the non-member gets zero rows, and the recursion error is gone from the Postgres logs.
Illustrative: the real diff is written against your repository. Full sample report
Still stuck on your own code?
Send the repository and the exact error. Fix: 290 EUR all taxes included, delivered within 72 hours, refunded if not resolved.
Not sure it is this error? Ask for a free written diagnosis, no card needed.