Insert or update on table violates foreign key constraint
A row points at a parent row that doesn’t exist, or something tried to delete a parent row that other rows still point at.
Also appears as
- update or delete on table "users" violates foreign key constraint "todos_user_id_fkey" on table "todos"
Prompt to fix it
Writes fail with "violates foreign key constraint". Read the error’s detail line to see which column, value, and table are involved. If it’s an insert, find where the code sets that ID and make it use a real parent row, such as the signed-in user from the session, and make sure parent rows are created first. If it’s a delete, ask me whether the child rows should be deleted too, kept without a parent, or block the delete, then update the constraint to match. Keep the foreign key, and show me the SQL before you run it.
Paste into Layerwise or any AI coding tool.
What it means
A foreign key links a column to a row in another table. todos.user_id might point to auth.users.id, so every todo belongs to a real user. Postgres enforces that link both ways: you can’t save a todo for a user who doesn’t exist, and you can’t delete a user who still has todos.
The detail line says which side you hit. A key that is not present in table "users" means the ID you saved has no matching parent. A key that is still referenced from table "todos" means a delete would leave rows pointing at nothing.
In Supabase, the delete case often shows up as “Database error deleting user” when you remove a user in the dashboard. The fix is to decide what should happen to that user’s rows: delete them too (on delete cascade), keep them without an owner (on delete set null), or keep blocking the delete on purpose.
Common causes
- The insert uses a placeholder ID, a test user’s ID, or an ID from another project instead of the signed-in user’s ID.
user_idpoints at aprofilestable, and the profile row doesn’t exist yet because the sign-up trigger failed or hasn’t run.- A user, project, or other parent row is deleted while child rows still point at it, and the key has no
on deleterule. - A seed or import script inserts child rows before their parents.
How to fix it
- Read the detail line to see the column, the value, and which table is missing the row or still uses it.
- For inserts, set the ID from the session (
supabase.auth.getUser()), or let the column default toauth.uid(). - If the key points at
profiles, make sure the profile row is created at sign-up before anything else is saved. - For deletes, recreate the foreign key with
on delete cascadeoron delete set null, or delete the child rows first. - In seed scripts, insert parent tables before child tables.
Example fix
-- Deleting a user now deletes their todos too.
-- To keep the rows instead, use "on delete set null"
-- (user_id must allow nulls).
alter table todos
drop constraint todos_user_id_fkey,
add constraint todos_user_id_fkey
foreign key (user_id) references auth.users (id) on delete cascade;Related errors
All errorsNull value in column violates not-null constraint
A save left a required column empty, and that column has no default value for Postgres to fill in.
Duplicate key value violates unique constraint
The app tried to save a row whose ID, email, or other unique value is already in the table, so Postgres rejected the copy.
New row violates row-level security policy
Postgres refused to save a row because no row-level security policy on that table lets the current user write it.
Relation does not exist
The query names a table that Postgres can’t find in this database, usually because it was never created here or has a different name.
Build with an agent that checks its work
The Layerwise agent clicks through the live preview and reads the console, so it catches errors before you do.