ERROR: insert or update on table "orders" violates foreign key constraint "orders_customer_id_fkey"
(SQLSTATE 23503) means the row points at a parent that does not exist: the
DETAIL line names the missing key, here customer 7. Insert the parent row
first, or fix the value you are sending.
The same code has a second face on the delete side: update or delete on table
"customers" violates foreign key constraint with is still referenced
in the detail. There you delete or move the child rows first, in one transaction.
The error
INSERT INTO orders VALUES (10, 7, 40.00);
psql:/tmp/p3.sql:14: ERROR: insert or update on table "orders" violates foreign key constraint "orders_customer_id_fkey"
DETAIL: Key (customer_id)=(7) is not present in table "customers".
And from the other direction, deleting a customer who still has an order:
DELETE FROM customers WHERE id = 7;
psql:/tmp/p3.sql:19: ERROR: update or delete on table "customers" violates foreign key constraint "orders_customer_id_fkey" on table "orders"
DETAIL: Key (id)=(7) is still referenced from table "orders".
Why it happens
customer_id integer NOT NULL REFERENCES customers (id) promises that every
order belongs to a real customer. PostgreSQL checks the promise on every insert or update
of orders (is the parent there?) and on every delete or key update of
customers (is anyone still pointing at it?). The constraint name
orders_customer_id_fkey is the one PostgreSQL generated from table and column,
so you can find it in \d orders.
Typical causes on the insert side: seeding child tables before parent tables, a hard-coded
id that exists on your machine but not in the test database, or a parent inserted in a
transaction that was rolled back. On the delete side, children still exist; here the
constraint had the default NO ACTION rule (confdeltype
a in pg_constraint).
The fix
INSERT INTO customers VALUES (7, 'David Chen');
INSERT 0 1
INSERT INTO orders VALUES (10, 7, 40.00);
INSERT 0 1
For the delete, remove the children first and the parent second, inside one transaction:
BEGIN;
BEGIN
DELETE FROM orders WHERE customer_id = 7;
DELETE 1
DELETE FROM customers WHERE id = 7;
DELETE 1
COMMIT;
COMMIT
If deleting a customer should always take the orders with it, declare the key with
ON DELETE CASCADE instead; that is a design decision, not a quick fix, because
one delete can then remove far more rows than you expect. From .NET, Npgsql raises a
PostgresException with SqlState 23503 and the
constraint name in ConstraintName.
How it was reproduced
A postgres:18 container (PostgreSQL 18.4, Debian build 18.4-1.pgdg13+1) on
Docker 29.9.2, with customers (id, name) and
orders (id, customer_id REFERENCES customers (id), total) and one fictional
customer. Inserting an order for customer 7 failed; inserting customer 7 first made it
pass; deleting that customer then failed until its order was deleted first. A verbose run
(\set VERBOSITY verbose) printed ERROR: 23503 with the
SCHEMA NAME, TABLE NAME and CONSTRAINT NAME fields.
Frequently asked
- What does 'Key is not present in table' mean in PostgreSQL?
- The foreign key value you inserted has no matching row in the parent table. Insert the parent first or correct the id; the DETAIL line names the column and the missing value.
- How do I fix 'is still referenced from table' when deleting?
- Delete or reassign the child rows first, then delete the parent, ideally in one transaction. If children should always go with the parent, change the foreign key to ON DELETE CASCADE.
- Which SQLSTATE is a foreign key violation in PostgreSQL?
- 23503, foreign_key_violation, for both the insert side and the delete side. Npgsql exposes it as PostgresException.SqlState together with the constraint name.
More decoded errors in the Fixes category. The SQL Server version of this error is error 547, and the delete rules are compared in one-to-many and ON DELETE.