Skip to content
🎉 Launch sale:50% off everything over $22 — automatically applied at checkout· ends Aug 2Shop the sale →
DevOps AI ToolKit
Newsletter
All guides
AI for Postgres By James Joyner IV · · 9 min read Last reviewed Jul 2026

Postgres Error: 'update or delete on table violates foreign key constraint' — Cause, Fix, and Troubleshooting Guide

Quick answer

Fix Postgres 'update or delete on table violates foreign key constraint': the parent row is still referenced by children. Repoint, cascade, or reorder.

Part of the PostgreSQL Database Errors hub
  • #postgres
  • #postgresql
  • #database
  • #troubleshooting
Free toolkit

Stuck on this Postgres error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

Overview

This error fires on the parent side of a foreign key. You tried to delete a parent row (or change its referenced key), but child rows still point at it, and the constraint has no ON DELETE/ON UPDATE action that would clean them up.

ERROR:  update or delete on table "customers" violates foreign key constraint "orders_customer_id_fkey" on table "orders"
DETAIL:  Key (id)=(42) is still referenced from table "orders".

The DETAIL line names the parent key value (id = 42) and the child table (orders) that still references it. By default a foreign key is ON DELETE NO ACTION, so Postgres blocks the operation rather than orphaning the children. Contrast this with the child-side error insert or update ... violates foreign key constraint, which fires when a child points at a parent that does not exist.

Symptoms

  • A DELETE FROM customers WHERE id = 42 or an UPDATE of a referenced primary key fails with the message above.
  • The DETAIL line reports Key (...) is still referenced from table "...".
  • Deleting a “leaf” row works, but deleting a row that other tables reference fails.
DELETE FROM customers WHERE id = 42;
ERROR:  update or delete on table "customers" violates foreign key constraint "orders_customer_id_fkey" on table "orders"
DETAIL:  Key (id)=(42) is still referenced from table "orders".

Common Root Causes

1. Child rows still reference the parent

The most common cause: orders still has rows whose customer_id = 42.

SELECT id, customer_id, status
FROM orders
WHERE customer_id = 42;
  id   | customer_id | status
-------+-------------+---------
 90311 |          42 | shipped
 90544 |          42 | paid
(2 rows)

2. The foreign key has no ON DELETE CASCADE or SET NULL

With the default NO ACTION, the parent cannot be removed while children exist; the constraint does not clean up for you.

3. Wrong deletion order

Deleting the parent before its children (instead of children first) triggers the block on any batch or manual cleanup.

4. Updating a referenced primary key

Changing customers.id from 42 to something else is treated the same as deleting 42: the old value is still referenced, so the update is rejected.

How to diagnose

Step 1: Find the referencing children

Query the child table directly using the key value from the DETAIL line.

SELECT count(*) AS referencing_rows
FROM orders
WHERE customer_id = 42;

If this returns anything above 0, those rows are what block the parent operation.

Step 2: Inspect the foreign key’s delete/update action

\d orders in psql shows the FK, or query pg_constraint for the machine-readable action codes.

SELECT conname,
       confdeltype,   -- a = NO ACTION, r = RESTRICT, c = CASCADE, n = SET NULL, d = SET DEFAULT
       confupdtype
FROM pg_constraint
WHERE conname = 'orders_customer_id_fkey';
        conname          | confdeltype | confupdtype
-------------------------+-------------+-------------
 orders_customer_id_fkey | a           | a
(1 row)

confdeltype = a (NO ACTION) confirms nothing will auto-clean the children.

Step 3: Enumerate every table that references the parent

A parent can be referenced by more than one child table; list them all before deleting.

SELECT conrelid::regclass AS child_table, conname
FROM pg_constraint
WHERE confrelid = 'customers'::regclass
  AND contype = 'f';

Fixes

Delete or repoint the children first

Remove the child rows (or move them to another parent) before deleting the parent.

DELETE FROM orders WHERE customer_id = 42;
DELETE FROM customers WHERE id = 42;

To reassign instead of delete:

UPDATE orders SET customer_id = 7 WHERE customer_id = 42;
DELETE FROM customers WHERE id = 42;

Add ON DELETE CASCADE or SET NULL to the FK

If children should follow the parent automatically, redefine the constraint. CASCADE deletes the children; SET NULL keeps them but clears the reference.

ALTER TABLE orders DROP CONSTRAINT orders_customer_id_fkey;
ALTER TABLE orders
  ADD CONSTRAINT orders_customer_id_fkey
  FOREIGN KEY (customer_id) REFERENCES customers (id)
  ON DELETE CASCADE;

Use a deferrable constraint for complex batch operations

A deferrable constraint checks at commit time, so intermediate states within one transaction are allowed.

ALTER TABLE orders
  ALTER CONSTRAINT orders_customer_id_fkey DEFERRABLE INITIALLY DEFERRED;

Adopt a soft-delete pattern

Instead of physically deleting referenced parents, mark them inactive so history and references stay intact.

ALTER TABLE customers ADD COLUMN deleted_at timestamptz;
UPDATE customers SET deleted_at = now() WHERE id = 42;

What to watch out for

  • ON DELETE CASCADE will silently delete child rows across every cascading table — confirm you actually want that data gone.
  • ON DELETE SET NULL requires the child FK column to be nullable; it fails against a NOT NULL column.
  • Deferring a constraint only relaxes when it is checked, not whether — the commit still fails if children remain unresolved.
  • Multiple child tables may reference the same parent; fix every one the pg_constraint query lists, not just the one named in the first error.
  • Updating a primary key that is referenced elsewhere is usually a design smell; prefer a stable surrogate key that never changes.
Free download · 368-page PDF

Fixed it? Get 500 Postgres & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.