error: new row violates row-level security policy for table "todos"
Node.js · updated 2026-11-10
Raised by Postgres (SQLSTATE 42501) when row-level security is on for the table and the row being inserted or updated is not allowed by any policy. node-postgres throws it, psycopg raises InsufficientPrivilege, and supabase-js returns it in the error field with code "42501" (PostgREST answers 403 with a session, 401 without one). Common right after enabling RLS on a Supabase table.
error: new row violates row-level security policy for table "todos"
at node_modules/pg/lib/client.js:694:17
at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
code: '42501'Quick fix
Find which policy is missing or failing. With RLS on and no INSERT policy, every insert is denied. With a policy, its WITH CHECK must be true for the new row: a check like (select auth.uid()) = user_id is null, so it fails, when the request has no signed-in session or the row has no user_id. If you read the row back (insert(...).select() in supabase-js, RETURNING in SQL), the table also needs a SELECT policy that allows that row.
- List the table policies (the Policies page in the Supabase dashboard, or select * from pg_policies where tablename = 'todos').
- No INSERT (or UPDATE) policy for the role making the request: add one. RLS with no matching policy denies everything.
- Check the request is signed in. Without a session the role is anon and auth.uid() is null, so an ownership check fails.
- Make sure the row carries the owner column the policy checks (user_id), set to the signed-in user.
- Reading the row back after writing (.select() or RETURNING) needs a SELECT policy too. An upsert also needs UPDATE and SELECT policies.
-- Supabase: let signed-in users insert and read their own rows
create policy "insert own todos" on todos
for insert to authenticated
with check ((select auth.uid()) = user_id);
create policy "read own todos" on todos
for select to authenticated
using ((select auth.uid()) = user_id);Why it happens
No policy for that command
RLS is enabled and there is a SELECT policy but no INSERT or UPDATE policy. Postgres denies any command no policy allows.
The check is false or null
The WITH CHECK expression fails for the new row: a different user_id, a missing user_id, or no session so auth.uid() is null. Null counts as a failure.
Reading the row back
The insert passes, but insert(...).select() or RETURNING needs the new row to pass a SELECT policy. Without one you get the same error, and the whole insert is rolled back.
Upsert takes the update path
INSERT ... ON CONFLICT DO UPDATE (supabase-js upsert) checks the INSERT, UPDATE and SELECT policies, so a table with only an INSERT policy fails on the second write.
Other fixes
- A secret (service role) key bypasses RLS only when the request carries no user access token; with a signed-in user's token the policies still apply. Keep secret keys in server code, never in the browser, and don't use one to paper over a missing policy.
- A missing GRANT fails with a different 42501 message, "permission denied for table", before any policy runs. Check grants first when a policy that looks right still fails.
- In supabase-js the error comes back in the error field (not thrown), so check it after every write and report it, or nothing records it.
- Test policies as the real role: set role authenticated and set request.jwt.claims in a SQL session, then run the same insert.
Same error, other wording
- psycopg.errors.InsufficientPrivilege: new row violates row-level security policy for table "todos"
- {"code":"42501","message":"new row violates row-level security policy for table \"todos\""}
- psycopg2.errors.InsufficientPrivilege: new row violates row-level security policy for table "todos"
Let your coding agent catch the next one
Runheed records this error in production with the stack trace, the breadcrumbs before it and, when you set it, the agent session that shipped the code, and serves it to Claude Code, Cursor or any MCP client. With it connected, you can ask:
> Find the row-level security errors, tell me which table and which command each came from, and add the missing INSERT, UPDATE or SELECT policies, or fix the code that sends rows without a session or a user_id.
Setup for Supabase Edge Functions, Node.js and Express or Vercel Functions, then have your agent fix and resolve it.
Related errors
- error: permission denied for table profiles
- error: infinite recursion detected in policy for relation "members"
- FunctionsHttpError: Edge Function returned a non-2xx status code
More in Databases
- PrismaClientKnownRequestError: Unique constraint failed on the fields: (`email`)Node.js
- PrismaClientInitializationError: Can't reach database server at `db.example.com:5432`Node.js
- MongoServerError: E11000 duplicate key error collection: app.users index: email_1 dup...Node.js
- [ioredis] Unhandled error event: Error: connect ECONNREFUSED 127.0.0.1:6379Node.js
- psycopg2.OperationalError: server closed the connection unexpectedlyPython
- django.db.utils.OperationalError: no such table: orders_orderPython
- psycopg.errors.UniqueViolation: duplicate key value violates unique constraint...Python
- error: relation "todo" does not existNode.js
- psycopg.errors.UndefinedColumn: column "titel" does not existPython
- error: null value in column "total" of relation "orders" violates not-null constraintNode.js
- error: insert or update on table "orders" violates foreign key constraint...Node.js
- error: invalid input syntax for type uuid: "undefined"Node.js
- sqlalchemy.exc.PendingRollbackError: This Session's transaction has been rolled back due...Python
- sqlite3.OperationalError: database is lockedPython
- pymongo.errors.ServerSelectionTimeoutError: localhost:27017: [Errno 111] Connection...Python
- redis.exceptions.ConnectionError: Error 111 connecting to localhost:6379. Connection...Python
References: PostgreSQL docs: CREATE POLICY (default deny, WITH CHECK, RETURNING and ON CONFLICT rules), PostgREST docs: error codes (42501 is 403 with a session, 401 without), Supabase docs: Row Level Security (grants come first, secret keys and user tokens)
Spotted a mistake? Email hello@greatwork.company. About Runheed.