error: infinite recursion detected in policy for relation "members"

Node.js · updated 2026-11-10

Raised by Postgres (SQLSTATE 42P17) when evaluating a row-level security policy needs that same policy again: a policy on members that selects from members, or two tables whose policies read each other. Every query on the table fails for the roles the policy applies to. node-postgres throws it, psycopg raises InvalidObjectDefinition, and supabase-js returns it in the error field. Most common with team or membership tables on Supabase.

error: infinite recursion detected in policy for relation "members"
    at node_modules/pg/lib/client.js:694:17
    at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
  code: '42P17'

Quick fix

Take the lookup out of the policy. Write a SECURITY DEFINER function that reads the membership table as its owner (so the policy is not applied inside it), put it in a schema your API does not expose, set its search_path to empty, and call that function from the policy.

  1. Find the policy that selects from its own table (or the pair of tables whose policies read each other): select * from pg_policies where tablename = 'members'.
  2. Create a private schema and a SECURITY DEFINER function there that returns what the policy needs, for example the current user's team ids.
  3. Set search_path = '' on the function and use schema-qualified names inside it.
  4. Replace the subquery in the policy with a call to the function.
  5. Test as the real role (set role authenticated) with two users in the same team and one outside it.
bash
-- The function reads members as its owner, so the policy doesn't run inside it
create schema if not exists private;

create function private.my_team_ids()
returns setof bigint
language sql
security definer
set search_path = ''
stable
as $$
  select team_id from public.members where user_id = (select auth.uid())
$$;

-- The policy runs as the caller, who needs to reach the function's schema
grant usage on schema private to authenticated;

drop policy "see teammates" on public.members;
create policy "see teammates" on public.members
  for select to authenticated
  using (team_id in (select private.my_team_ids()));

Why it happens

A policy that reads its own table

A "members can see their teammates" policy written as team_id in (select team_id from members where ...) needs the members policy to evaluate the members policy.

Two policies that read each other

The projects policy checks members, and the members policy checks projects. Neither resolves.

A helper view or function without SECURITY DEFINER

Moving the subquery into a normal view or function doesn't help: it still runs as the caller, so the policy still applies inside it.

Other fixes

  • Keep SECURITY DEFINER functions out of schemas your API exposes (on Supabase, not in public), or anyone can call them over the API with the owner's rights.
  • In supabase-js the error comes back in the error field, not thrown, so a list query can quietly show nothing unless you check it.

Same error, other wording

  • psycopg.errors.InvalidObjectDefinition: infinite recursion detected in policy for relation "members"
  • {"code":"42P17","message":"infinite recursion detected in policy for relation \"members\""}

Let your coding agent catch the next one

Runheed records this error in production with the stack trace, the breadcrumbs before it and, when you set it, the agent session that shipped the code, and serves it to Claude Code, Cursor or any MCP client. With it connected, you can ask:

> Find the "infinite recursion detected in policy" errors, show me the policies on that table that read the table itself or each other, and replace the lookup with a SECURITY DEFINER function in a private schema.

Setup for Node.js and Express or Vercel Functions, then have your agent fix and resolve it.

Related errors

More in Databases

References: Supabase docs: Row Level Security (security definer functions, recursion between tables), PostgreSQL docs: CREATE FUNCTION (SECURITY DEFINER and search_path), PostgreSQL docs: CREATE POLICY

Spotted a mistake? Email hello@greatwork.company. About Runheed.