The "PGRST118: Could not find function" error occurs when you attempt to call a PostgreSQL function via Supabase's PostgREST API, but the function cannot be found in the schema cache. This typically happens after creating a new function, due to schema cache not being refreshed, or when the function lacks proper permissions.
The "PGRST118: Could not find function" error is a PostgREST error that indicates the REST API layer cannot locate or call the PostgreSQL function you're requesting. PostgREST is the REST API layer that Supabase uses to expose your PostgreSQL database functions as API endpoints. When you call a custom PostgreSQL function via the RPC endpoint (like `/rest/v1/rpc/my_function`), PostgREST searches for the function in its internal schema cache. If the function cannot be found, you receive this error. This error commonly occurs when: 1. **Schema cache is stale** - You created a new function but PostgREST hasn't reloaded the schema cache yet 2. **Function lacks execute permissions** - The function exists but the appropriate database role (anon/authenticated/service_role) doesn't have EXECUTE permissions 3. **Function is in a non-public schema** - The function exists in a custom schema that PostgREST isn't searching 4. **Parameter name case mismatch** - The parameter names in your RPC call don't match the function definition (PostgreSQL is case-sensitive) 5. **Function signature mismatch** - You're calling the function with a different number or type of parameters than defined
After creating or modifying a PostgreSQL function, PostgREST's schema cache needs to be refreshed. Try reloading the schema cache using the NOTIFY command:
-- Execute this in the Supabase SQL Editor
NOTIFY pgrst, 'reload config';Wait 5-10 seconds after running this command for the schema cache to refresh. Then retry your RPC call.
If the NOTIFY command doesn't work immediately, you can also try:
-- Force PostgREST backend to reload
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE application_name = 'postgrest';This terminates the PostgREST process, which will automatically restart and reload the schema cache. Wait 10-15 seconds before retrying.
Check that your function is properly defined in PostgreSQL:
-- List all functions in the public schema
SELECT n.nspname as schema_name,
p.proname as function_name,
pg_get_functiondef(p.oid) as function_definition
FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE n.nspname = 'public'
AND p.proname = 'my_function';Verify the function:
- Exists and appears in the results
- Has correct parameter names and types
- Compiles without syntax errors
Example function definition:
CREATE OR REPLACE FUNCTION my_function(p_user_id INT)
RETURNS TABLE (user_name TEXT, email TEXT) AS $$
BEGIN
RETURN QUERY
SELECT name, email FROM users WHERE id = p_user_id;
END;
$$ LANGUAGE plpgsql;Ensure the database role has permission to execute the function. PostgREST uses different roles depending on your authentication:
-- For authenticated users
GRANT EXECUTE ON FUNCTION my_function(p_user_id INT) TO authenticated;
-- For public/anonymous users
GRANT EXECUTE ON FUNCTION my_function(p_user_id INT) TO anon;
-- For service role (admin operations)
GRANT EXECUTE ON FUNCTION my_function(p_user_id INT) TO service_role;
-- For all roles
GRANT EXECUTE ON FUNCTION my_function(p_user_id INT) TO PUBLIC;Check current permissions:
-- Check function permissions
SELECT grantee, privilege_type
FROM information_schema.role_routine_grants
WHERE routine_schema = 'public'
AND routine_name = 'my_function';PostgreSQL function parameter names are case-sensitive. When calling via RPC, parameter names must exactly match the function definition.
Bad example (case mismatch):
-- Function defined with lowercase
CREATE OR REPLACE FUNCTION get_user(p_user_id INT)
RETURNS TABLE (id INT, name TEXT) AS $$
...// JavaScript - WRONG: using camelCase when function expects snake_case
const { data, error } = await supabase.rpc('get_user', { pUserId: 123 });
// Error: PGRST118 - could not find functionCorrect example:
// JavaScript - CORRECT: matching parameter names exactly
const { data, error } = await supabase.rpc('get_user', { p_user_id: 123 });Best practice:
- Use lowercase with underscores for PostgreSQL function names and parameters
- Avoid mixed case in PostgreSQL identifiers
- Match the exact parameter names in your RPC calls
If your function is in a custom schema (not 'public'), PostgREST may not find it by default:
-- Find what schema your function is in
SELECT n.nspname as schema_name, p.proname as function_name
FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE p.proname = 'my_function';If the function is in a custom schema:
1. Move it to the public schema (simplest):
CREATE OR REPLACE FUNCTION public.my_function(p_user_id INT)
RETURNS TABLE (id INT, name TEXT) AS $$
...2. Or expose the custom schema to the API:
- Go to Supabase Dashboard → API Settings
- Add your custom schema to the exposed schemas list
- Then call: supabase.rpc('my_function', {}, { schema: 'my_schema' })
The function parameters in your RPC call must match the PostgreSQL function signature exactly:
-- Function definition with specific parameters
CREATE OR REPLACE FUNCTION add_numbers(a INT, b INT)
RETURNS INT AS $$
SELECT a + b;
$$ LANGUAGE sql;// WRONG: Missing required parameter
const { data, error } = await supabase.rpc('add_numbers', { a: 5 });
// Error: PGRST118
// CORRECT: All required parameters provided
const { data, error } = await supabase.rpc('add_numbers', { a: 5, b: 3 });
// CORRECT: Parameterless function
const { data, error } = await supabase.rpc('get_current_timestamp', {});Check your function signature:
-- View function details including parameter count
SELECT p.proname,
p.pronargs as parameter_count,
pg_get_functiondef(p.oid) as definition
FROM pg_proc p
WHERE p.proname = 'my_function';## Understanding PostgREST Function Discovery
PostgREST maintains an in-memory schema cache of all available PostgreSQL functions. When you call a function via RPC, PostgREST:
1. Receives the RPC request with function name and parameters
2. Searches the schema cache for a matching function
3. Validates that the current user/role has EXECUTE permission
4. Executes the function and returns results
If the function isn't found in the cache, PGRST118 is returned.
## Schema Cache Lifecycle
- Loaded on startup: PostgREST reads all functions from PostgreSQL schema
- Cached in memory: Queries use the cache, not live schema queries
- Manually refreshed: Use NOTIFY pgrst, 'reload config' to refresh
- Process restart: Terminating PostgREST forces a full reload
This is why newly created functions sometimes don't work immediately—the cache is stale.
## Best Practices for Custom Functions
1. Use public schema: Keep functions in 'public' for simpler PostgREST integration
2. Use consistent naming: Use lowercase with underscores (PostgreSQL convention)
3. Document parameters: Add comments to function definitions
4. Set permissions after creation: Add GRANT statements right after CREATE
5. Test before deployment: Call functions in SQL Editor first
6. Use parameter type hints: Be explicit about parameter types
-- Good example: Well-documented, properly scoped
CREATE OR REPLACE FUNCTION public.get_user_by_email(
p_email TEXT -- User email address
)
RETURNS TABLE (
id UUID,
email TEXT,
created_at TIMESTAMP
) AS $$
SELECT id, email, created_at FROM users WHERE email = p_email;
$$ LANGUAGE sql STABLE;
-- Grant permissions
GRANT EXECUTE ON FUNCTION public.get_user_by_email(TEXT) TO authenticated;
GRANT EXECUTE ON FUNCTION public.get_user_by_email(TEXT) TO anon;## Debugging RPC Calls
Use Supabase's Logs to see detailed error messages:
1. Go to Supabase Dashboard → Logs
2. Filter by "API" or "PostgREST"
3. Look for error details with full message
4. Check Authorization header and function name
## Common PostgREST Function Errors
| Error | Meaning | Fix |
|-------|---------|-----|
| PGRST118 | Function not found | Reload schema cache or check permissions |
| PGRST202 | Could not find function (different format) | Same as PGRST118 |
| 401 Unauthorized | Missing/invalid JWT | Check auth token |
| 403 Forbidden | Permission denied on function | Grant EXECUTE permission |
| 400 Bad Request | Invalid parameter types | Check parameter types match function signature |
email_conflict_identity_not_deletable: Cannot delete identity because of email conflict
How to fix "Cannot delete identity because of email conflict" in Supabase
mfa_challenge_expired: MFA challenge has expired
How to fix "mfa_challenge_expired: MFA challenge has expired" in Supabase
conflict: Database conflict, usually related to concurrent requests
How to fix "database conflict usually related to concurrent requests" in Supabase
phone_exists: Phone number already exists
How to fix "phone_exists" in Supabase
StorageApiError: resource_already_exists
StorageApiError: Resource already exists