Fix guides

Supabase RPC returns 404: "Could not find the function in the schema cache" (PGRST202)

Your app calls supabase.rpc() and gets a 404 with PGRST202, even though the function exists. Why it happens in Lovable, Bolt, and Cursor apps, and how to fix the argument names, schema, and cache.

What you are seeing

Why it happens

  1. PostgREST, the layer behind supabase.rpc(), matches a function by its name and every argument name. If the app sends userId and the function declares user_id, there is no match. The names in brackets in the error are the ones your app sent.
  2. A value in the call was undefined. JSON drops undefined keys, so that argument is never sent and the call matches no function. Arguments without a default must all be sent.
  3. The function lives in a schema other than public (or the code calls .rpc('billing.get_orders')), so PostgREST searches the wrong place.
  4. Two versions of the function exist with different arguments (overloads) after a migration added a parameter instead of replacing the function, and the call is ambiguous or matches the old one.
  5. Less often, the schema cache really is stale after a change. On Supabase it normally reloads by itself when a function is created or changed, so check the names first.

How to fix it

1. Compare what you sent with what the function declares

select p.proname, n.nspname as schema,
       pg_get_function_identity_arguments(p.oid) as args
from pg_proc p
join pg_namespace n on n.oid = p.pronamespace
where p.proname = 'get_orders';

2. Send exactly those argument names, with real values

// function declared as get_orders(user_id uuid)
const { data, error } = await supabase.rpc('get_orders', {
  user_id: session.user.id, // not userId, and never undefined
});

3. Give optional arguments a default

create or replace function public.get_orders(user_id uuid, status text default null)
returns setof public.orders
language sql stable
as $$
  select * from public.orders o
  where o.user_id = get_orders.user_id
    and (get_orders.status is null or o.status = get_orders.status);
$$;

4. Drop the old overload if a migration left two

-- list them with the query from step 1, then drop the one you no longer call
drop function public.get_orders(uuid);

5. Call functions outside public the right way

const { data, error } = await supabase
  .schema('billing')
  .rpc('get_orders', { user_id });

// billing must be listed under Exposed schemas in the API settings,
// otherwise the error becomes PGRST106 "Invalid schema"

6. Only then, reload the schema cache

notify pgrst, 'reload schema';

-- if that does nothing, Supabase's troubleshooting guide suggests:
select pg_notification_queue_usage();

7. Keep the function safe once it works

If the function returns user data, keep it security invoker (the default) so RLS applies, and grant execute only to the roles that need it. If the error changes to 42501 "permission denied for function", the name now matches and only the grant is missing: grant execute on function public.get_orders(uuid) to authenticated;

How to know it is fixed

The same supabase.rpc() call returns data with no error, the query from step 1 shows exactly one version of the function, and a signed-in test user only gets their own rows back. Sources: Supabase troubleshooting: PostgREST not recognizing new functions and PostgREST error codes.