Short answer: Supabase blocked the save because no row-level security (RLS) policy allowed that user to write that row, or to read it back afterwards. The fix is to add the right policy. Do not switch RLS off to make the error go away. That leaves your table open to anyone with your public key.
What the error looks like
In the browser's Network tab (F12 > Network), the failing request to /rest/v1/<table> returns 403 (sometimes shown as 401) with:
{ "code": "42501", "message": "new row violates row-level security policy for table \"profiles\"" }It often works in the Supabase SQL editor. That's because the SQL editor runs as an admin that skips RLS, so it doesn't tell you much.
The causes, in the order to check them
1. There is no INSERT policy for that table
Lovable often creates a SELECT policy so the app can read, and stops there. Add an insert policy for signed-in users:
create policy "Users insert own rows"
on public.your_table for insert to authenticated
with check ((select auth.uid()) = user_id);2. The app isn't sending user_id, or sends the wrong one
The policy compares the signed-in user's id with the row's user_id. If the insert leaves it out, it's null and the check fails. Easiest fix, let the database fill it in:
alter table public.your_table alter column user_id set default auth.uid();3. The insert asks for the row back (.select())
Lovable code often looks like supabase.from('posts').insert(row).select().single(). Asking for the row back means Postgres also checks your SELECT policy on the new row. If no SELECT policy lets that user see it, you get the same 42501 error. Add one:
create policy "Users read own rows"
on public.your_table for select to authenticated
using ((select auth.uid()) = user_id);Or remove .select() if you don't need the row back.
4. It's an upsert or ON CONFLICT
An upsert can become an update, so it also needs an UPDATE policy (with both using and with check) and a matching SELECT policy.
5. The user isn't actually signed in at that moment
If the policy says to authenticated but the request goes out before the session is ready, it arrives as the anonymous role and fails. Make sure a session exists before saving. Don't widen the policy to anon for account data.
6. It's a file upload
Storage uploads write to storage.objects, so the policies need to be on that table, for your bucket. Uploads also read the row back, so add a SELECT policy for the bucket as well as INSERT.
A public form (waitlist, contact form)?
If anyone should be able to submit without logging in, an anon insert policy is fine for that table only:
create policy "Anyone can submit"
on public.waitlist for insert to anon
with check (true);Keep inserts plain (no .select()), or the anon role will also need read access to the table.
How to check it's fixed
Sign in as a normal test user in a private window on your live site, save again, and confirm the request now returns 201 and the row appears. Then sign in as a second user and check they can't see the first user's rows.