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.