VibeRaven

What does function_search_path_mutable mean, and how do I fix it?

By Ohad Krispin, founder of VibeRaven · LinkedIn

function_search_path_mutable is the Supabase Security Advisor warning for a function with no search_path setting, so an unqualified name in its body, such as todos instead of public.todos, resolves through the search_path of whoever calls it. The fix is set search_path = '' on the function, with a schema on every table and function name in the body. Setting it alone clears the warning, but breaks a function whose body still uses unqualified names.

psql output from a Supabase Postgres 17.11 container: lint 0011 flags count_todos, and an empty search_path clears the lint but the call fails because todos does not resolve. The rewrite that reads public.todos returns 1.

It is one of several Security Advisor warnings. For the others, see what each Supabase Security Advisor warning means.

How to fix function_search_path_mutable, then check the fix

  • Recreate the function with set search_path = '' and a schema on every name in its body. Supabase's lint docs recommend the empty string, "which forces all references within the function's body to be fully qualified". For a trigger that creates a profile row at sign-up:
    create or replace function public.handle_new_user()
    returns trigger
    language plpgsql
    security definer
    set search_path = ''
    as $$
    begin
      insert into public.profiles (id) values (new.id);
      return new;
    end;
    $$;
  • If the body already names every schema, one statement is enough. List the argument types if the function has any:
    alter function public.count_todos() set search_path = '';
  • Call each function once after the change. On a Supabase Postgres 17.11 container on 20 October 2026, a function whose body said todos instead of public.todos lost the warning, then failed with relation "todos" does not exist. Write auth.uid(), not uid(). Built-ins such as count() and now() work without a schema, because Postgres always searches pg_catalog.
  • Confirm the setting. proconfig should list search_path:
    select proname, proconfig
    from pg_proc
    where proname = 'count_todos';
  • Run the Security Advisor again. The function should be gone from the list:
    supabase db advisors --linked --type security

When function_search_path_mutable is low risk

The lint's source flags every function without a search_path setting in a schema that is not a system schema, security definer or not, whether the API exposes it or not.

Security invoker functions

A security invoker function, the Postgres default, runs with the caller's own rights, so a name it resolves the wrong way gains the caller nothing new. Security definer functions run as their owner, so fix those first. This lists the flagged functions in public, security definer first:

select p.oid::regprocedure as function, p.prosecdef as security_definer
from pg_catalog.pg_proc p
join pg_catalog.pg_namespace n on n.oid = p.pronamespace
where n.nspname = 'public'
  and not exists (
    select 1 from unnest(coalesce(p.proconfig, '{}')) as c
    where c like 'search_path=%'
  )
order by p.prosecdef desc, 1;

A search_path that is set, but not empty

The lint checks only that search_path is set: on the test container, a function with set search_path = public was not flagged. For a security definer function, Postgres's CREATE FUNCTION docs say to leave out any schema untrusted users can write to and to put pg_temp last. Supabase recommends the empty string.

Related guides

Sources

Can a caller of my API exploit a mutable search_path?

Supabase's lint docs say malicious users "could potentially exploit" it, and rate it WARN, not ERROR. That needs a role that can create objects in a schema the function searches; on the test container, anon and authenticated could not create objects in public. Fix it anyway: it is one line, and the function then behaves the same for every caller.

Does the warning cover functions outside public?

Yes. The lint checks every schema except system ones such as auth, storage, extensions and pg_catalog, including a private schema the API does not expose. It skips functions that belong to an extension.