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
supabase.rpc('get_orders', ...)fails with HTTP 404 and{"code":"PGRST202","message":"Could not find the function public.get_orders(userId) in the schema cache"}.- The
detailsfield says it "searched for the function ... but no matches were found in the schema cache", and thehintmay suggest a function with different argument names. - The function runs fine in the SQL editor, and normal table queries from the app keep working.
- It broke right after your AI tool renamed a parameter, added one, or created the function in a different schema.
Why it happens
- PostgREST, the layer behind
supabase.rpc(), matches a function by its name and every argument name. If the app sendsuserIdand the function declaresuser_id, there is no match. The names in brackets in the error are the ones your app sent. - 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. - The function lives in a schema other than
public(or the code calls.rpc('billing.get_orders')), so PostgREST searches the wrong place. - 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.
- 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.