Fix the 'Database Error Saving New User' Signup Failure in Lovable
If signup fails with the error 'Database error saving new user', the problem is almost never the user's email or password. It is a server-side failure that happens after Supabase Auth accepts the credentials: a broken handle_new_user trigger on auth.users, or a Row Level Security policy that blocks the insert into your profiles table. This guide gives you the exact SQL to find and fix both.
By Founder Name · Last verified: 2026-06-25
Why does Lovable signup fail with 'Database error saving new user'?
Because a database operation that runs the instant a user is created is failing. Supabase Auth accepted the email and password and wrote the row to auth.users, but a trigger or policy attached to that event threw an error. Supabase then rolls the whole signup back and returns the generic 'Database error saving new user' message instead of the real cause.
Lovable almost always wires up a Postgres trigger called handle_new_user that fires after a row is inserted into auth.users. Its job is to copy the new user into a public profiles (or users) table so the rest of your app has a row to read. When that trigger function errors, the entire transaction is aborted: the auth row is rolled back, the user never gets created, and the front end sees the opaque failure.
The error string is deliberately generic for security reasons, so the message itself tells you nothing about the root cause. The real reason is sitting in the Postgres logs. There are only three things that realistically cause it: the trigger function references a column or table that does not exist, the trigger function is not allowed to insert into profiles because of Row Level Security, or a NOT NULL or UNIQUE constraint on profiles is being violated. Each has a precise fix below.
| What you see | Most likely cause | Fix |
|---|---|---|
| Error on every signup attempt | handle_new_user trigger function throws on insert | Read Postgres logs, fix the function body |
| Logs show 'column ... does not exist' | Trigger inserts a column your profiles table lacks | Add the column or remove it from the insert |
| Logs show 'permission denied for table profiles' | Trigger runs without SECURITY DEFINER and RLS blocks it | Recreate the function as SECURITY DEFINER |
| Logs show 'new row violates row-level security policy' | No INSERT policy allows the profiles row | Add a profiles insert policy or use SECURITY DEFINER |
| Logs show 'null value in column ... violates not-null' | Trigger does not supply a required NOT NULL column | Give the column a default or set it in the trigger |
| Logs show 'duplicate key value violates unique constraint' | A profile row for that id or email already exists | Use insert ... on conflict do nothing |
| No trigger exists, signup still fails on profile save | Client-side insert into profiles blocked by RLS | Add an INSERT policy keyed to auth.uid() |
How do I find the real error behind 'Database error saving new user'?
The front-end message is a decoy. The actual Postgres error that aborted the signup is in your Supabase project logs. Open the Supabase dashboard, go to Logs, and read the Postgres or Auth logs for the timestamp of your failed signup. You will see the specific column, table, constraint, or policy that threw. That one line tells you exactly which fix below to apply.
- Open the Supabase dashboard for the project Lovable connected (not the Lovable editor).
- Go to Logs in the left sidebar, then open Postgres Logs.
- Attempt one signup in your app to generate a fresh failure with a known timestamp.
- Find the matching log line and read the error: note the table, column, or constraint named.
- Match that error string to the diagnostic table above to pick the correct fix.
Related: fix broader Supabase errors in Lovable · Supabase RLS permission errors
How do I fix a broken handle_new_user trigger?
Recreate the trigger function so it runs as SECURITY DEFINER, sets a safe search_path, and inserts only columns that actually exist on your profiles table. SECURITY DEFINER lets the function bypass Row Level Security when it writes the profile row, which is the correct pattern for a signup trigger. Run the SQL below in the Supabase SQL Editor and signup will succeed again.
First, inspect your profiles table so you insert the right columns. A function that tries to write a full_name column the table does not have will throw 'column does not exist' and abort every signup. Run this to see what columns exist:
select column_name, is_nullable, data_type from information_schema.columns where table_schema = 'public' and table_name = 'profiles';
Now recreate the function and trigger. SECURITY DEFINER makes the function execute with the privileges of its owner (the postgres role), so it is not blocked by RLS on profiles. The explicit search_path is a security best practice that prevents a hijacked search path from redirecting the insert. Adjust the inserted columns to match what your table actually has:
create or replace function public.handle_new_user()\nreturns trigger\nlanguage plpgsql\nsecurity definer\nset search_path = public\nas $$\nbegin\n insert into public.profiles (id, email)\n values (new.id, new.email)\n on conflict (id) do nothing;\n return new;\nend;\n$$;\n\ndrop trigger if exists on_auth_user_created on auth.users;\n\ncreate trigger on_auth_user_created\n after insert on auth.users\n for each row execute function public.handle_new_user();
The on conflict (id) do nothing clause makes the trigger idempotent: if a profile row somehow already exists for that user id, the signup still succeeds instead of throwing a duplicate-key error. Only insert the id and email here unless every other column has a default or is supplied; a NOT NULL column with no default and no value is the second most common cause of this failure.
How do I fix the profiles insert policy so the row saves?
If your app inserts the profile from the client instead of a trigger, Row Level Security blocks it unless you have an INSERT policy that allows it. The policy must let an authenticated user insert a row whose id matches their own auth.uid(). Enable RLS on profiles, then add a with check policy keyed to auth.uid() so each user can create only their own profile row.
Run this in the Supabase SQL Editor. The first statement enables RLS (leaving it off is a security hole), and the policy allows an authenticated user to insert exactly one profile row that belongs to them. The with check clause is what RLS evaluates on INSERT:
alter table public.profiles enable row level security;\n\ncreate policy \"Users can insert their own profile\"\n on public.profiles\n for insert\n to authenticated\n with check (auth.uid() = id);\n\ncreate policy \"Users can view their own profile\"\n on public.profiles\n for select\n to authenticated\n using (auth.uid() = id);
If you use the SECURITY DEFINER trigger from the previous section, you do not strictly need an INSERT policy for signup, because the trigger bypasses RLS. But you still want the SELECT policy so the app can read the profile after login. Pick one pattern: a SECURITY DEFINER trigger that owns profile creation, or a client-side insert protected by an INSERT policy. Mixing both without care is how the auth.uid() check silently fails and the row never lands.
Why does it work in preview but fail for real signups?
Because the trigger and policies live in the database, and the database is shared by both the preview and the deployed app. If it works in preview but breaks live, the difference is usually environment variables, not SQL: your deployed build is pointed at a different Supabase project, or VITE_SUPABASE_URL and the anon key were never added to your host. Same code, wrong database.
We call this The Vanishing Env-Var: the Supabase URL and anon key exist in the Lovable editor environment, so signup works there, but they were never copied into your production hosting provider's environment settings. The deployed client then talks to the wrong project (or no project), and the trigger you carefully fixed is not even in the database it is hitting. Confirm both environments point at the same Supabase project ref before assuming the SQL is wrong.
The reverse also happens: you fixed the trigger in your local or staging Supabase project, but Lovable is connected to a different one. Always apply the SQL in the exact project whose URL appears in your deployed app's VITE_SUPABASE_URL. When the project ref matches and the keys are present, a trigger or policy fix takes effect immediately for every new signup.
| Symptom | Likely environment cause | Check |
|---|---|---|
| Works in Lovable preview, fails on live site | Production env vars missing or pointing elsewhere | Compare VITE_SUPABASE_URL in editor vs host |
| Worked locally, fails after deploy | Deploy serving a stale commit without the SQL change | Confirm the database project ref, not just code |
| Fails everywhere after a Supabase project swap | App keys not rotated to the new project | Update anon key and URL to the active project |
How do I confirm signup is actually fixed?
Do not trust a single successful signup in the editor. Run a full verification: create a brand-new user from the deployed app, confirm the auth row and the profile row both exist, and check that no error appears in the Postgres logs. The most common false positive is a signup that returns success while the profile row silently failed to save under RLS.
- Sign up with a brand-new email from the deployed (not preview) build.
- In Supabase, open Authentication and confirm the new user appears in the users list.
- Open the Table Editor and confirm a matching row exists in profiles with the correct id.
- Re-open Postgres Logs and confirm there is no error at the signup timestamp.
- Sign out, sign back in, and confirm the app reads the profile without a redirect loop.
- Repeat once more with a second new email to rule out a one-off success.
When should I get an engineer to fix this instead?
If you have read the Postgres logs, applied the trigger and policy fixes, and signup still fails, the cause is structural: a conflicting older trigger, a profiles schema that drifted from what the app expects, or a deeper RLS misconfiguration that is also exposing data. At that point a senior engineer can fix it in one pass and audit the rest of your auth and RLS setup while they are in there.
Signup is the front door of your product, so a broken handle_new_user trigger is an emergency: every new user who tries to register is silently turned away, and you may not even see it happening. If your app stores anything sensitive, a misconfigured profiles policy often signals broader RLS gaps where one user can read another's data. We fix the immediate signup failure first, then review your full Supabase Row Level Security setup so the same class of bug cannot recur.
Related: our Lovable app rescue service · book an emergency audit call
Frequently asked questions
What does 'Database error saving new user' mean in Lovable?
Is 'Database error saving new user' a problem with my password or email?
Where do I find the real error in Supabase?
What is the handle_new_user trigger and why does it break?
Why does making the trigger SECURITY DEFINER fix it?
Do I need an insert policy on profiles if I use the trigger?
Why does signup work in the Lovable preview but fail on my live site?
Could this error mean my app has a security problem?
How long does it take to fix 'Database error saving new user'?
Can I just keep clicking Fix in Lovable to resolve this?
App down or leaking data? Get an expert on it within 24–48h.
Book a free 30-minute audit call. We'll diagnose what's wrong and tell you exactly what it costs to fix.