devto 2026-10-19 원문 보기 ↗
If you built your app with Lovable or Bolt on top of Supabase, sooner or later you'll see this:
new row violates row-level security policy for table "todos"
That's Postgres error code 42501. Its quieter cousin is worse: a select that returns [] with no error, even though the table clearly has rows.
When Row Level Security (RLS) is enabled and no policy allows an action, Supabase blocks it. Common causes in AI-built apps:
insert/select policy existsauth.uid() is nulluser_id, or sets it to a different valueupdate policy is missing its with check
Reads and writes fail differently. A blocked select doesn't throw; rows are filtered out and you get []. A blocked insert throws the error above.
Run this in the Supabase SQL editor (rename todos to your table):
alter table public.todos enable row level security;
create policy "Users read own todos" on public.todos
for select to authenticated using ((select auth.uid()) = user_id);
create policy "Users insert own todos" on public.todos
for insert to authenticated with check ((select auth.uid()) = user_id);
create policy "Users update own todos" on public.todos
for update to authenticated
using ((select auth.uid()) = user_id) with check ((select auth.uid()) = user_id);
create policy "Users delete own todos" on public.todos
for delete to authenticated using ((select auth.uid()) = user_id);
user_id automaticallyalter table public.todos alter column user_id set default auth.uid();
const { data: { user } } = await supabase.auth.getUser();
if (!user) throw new Error('Not signed in');
await supabase.from('todos').insert({ title, user_id: user.id });
The service_role key (and the newer sb_secret_... keys) bypasses RLS entirely. Keep it on the server only, never in frontend code. The browser should only use the anon or publishable key, and that key is only safe because RLS limits what it can do.
Write Supabase RLS policies so authenticated users can only select, insert, update, and delete rows where user_id = auth.uid(). Don't disable RLS and don't use the service_role key in the client.
Why do I get empty results instead of an error? RLS filters select results silently. With no policy, you get zero rows.
Is a public read policy OK? Yes, for genuinely public data: for select to anon, authenticated using (true).
Originally published on FixKit AI. If you're seeing a different error, paste it into the free FixKit Error Fixer (disclosure: I built it). Related: CORS errors when a Lovable app calls an API.