Postgres error 42501: insufficient privilege
permission denied for table x / new row violates row-level security policy for table "x"
The current role is not allowed to do this: either it lacks a privilege (GRANT), or a row level security policy rejected the row.
Common causes
- permission denied for table: the role (often anon or authenticated) has no GRANT on the table, schema or sequence.
- new row violates row-level security policy: RLS is on and no INSERT or UPDATE policy WITH CHECK accepts the new row, often because user_id is not set to the caller.
- The request ran as anon because the user token was missing or expired.
- permission denied for schema: the role lacks USAGE on a schema such as a private or extensions schema.
How to fix it
- Grant what the API role needs: grant select, insert on public.todos to authenticated;
- Write a policy whose WITH CHECK matches the rows the client sends, and set ownership columns with a default so clients cannot get them wrong.
- Make sure the client sends the signed-in user's token (call the query after the session is restored).
- Test the policy as the role: set role authenticated; set request.jwt.claims to the user's claims, then run the statement.
alter table todos enable row level security;
create policy "owners insert" on todos for insert to authenticated
with check (user_id = auth.uid());
create policy "owners read" on todos for select to authenticated
using (user_id = auth.uid());
alter table todos alter column user_id set default auth.uid();Through a REST API
PostgREST, the REST layer behind supabase-js and Scribase, answers HTTP 401 without a user token, 403 with one for this error and returns the SQLSTATE in the code field of the JSON error body. PostgREST answers 401 when the request had no user token and 403 when it had one. In supabase-js, an insert followed by .select() also needs a SELECT policy that returns the new row.
Questions
What does Postgres error 42501 mean?
42501 is insufficient_privilege in class 42 (Syntax Error or Access Rule Violation). The current role is not allowed to do this: either it lacks a privilege (GRANT), or a row level security policy rejected the row.
How do I fix 42501?
Grant what the API role needs: grant select, insert on public.todos to authenticated; Write a policy whose WITH CHECK matches the rows the client sends, and set ownership columns with a default so clients cannot get them wrong. Make sure the client sends the signed-in user's token (call the query after the session is restored). Test the policy as the role: set role authenticated; set request.jwt.claims to the user's claims, then run the statement.
What HTTP status does a REST API return for 42501?
PostgREST, the REST layer behind supabase-js, answers 401 without a user token, 403 with one.