How to fix ERROR: cannot truncate a table referenced in a foreign key constraint in PostgreSQL

PostgreSQLINTERMEDIATEMEDIUMUpdated Dec 29, 2025· 187 views

This error occurs when you try to TRUNCATE a table that is referenced by another table through a foreign key constraint. TRUNCATE is much faster than DELETE but cannot proceed if child tables have references. Use CASCADE to truncate all related tables together, truncate tables in dependency order, or temporarily disable constraints.

Quick fix

Fixes ERROR: cannot truncate a table referenced in a foreign key constraint

ERROR: cannot truncate a table referenced in a foreign key constraint
DETAIL: Table "orders" references "customers" via foreign key "orders_customer_id_fkey".
  1. Check the error message to identify which table has the foreign key reference
  2. Use CASCADE to truncate all dependent tables automatically
  3. Alternatively, truncate specific tables in dependency order
  4. Use RESTART IDENTITY to reset sequence counters
postgresql — error
ERROR: cannot truncate a table referenced in a foreign key constraint
DETAIL: Table "orders" references "customers" via foreign key "orders_customer_id_fkey".
ERROR: cannot truncate a table referenced in a foreign key constraint

What this error means

PostgreSQL's TRUNCATE statement is an optimized way to delete all rows from a table without scanning it individually. However, TRUNCATE prevents data integrity violations by refusing to truncate tables that are referenced by foreign keys from other tables. When PostgreSQL detects that you're trying to truncate a table with incoming foreign key references, it raises this error. This is a safety feature—truncating a parent table while child records still reference it would break referential integrity.

Typical symptoms

  • TRUNCATE TABLE command fails with a foreign key constraint error
  • Error message shows the name of the table referencing the one you're trying to truncate
  • DELETE FROM works fine, but TRUNCATE does not
  • The constraint name is included in the error, showing which relationship is blocking the operation

Common causes

  1. Attempting to TRUNCATE a table that has foreign key references coming FROM other tables (is a parent table)
  2. Not using the CASCADE option needed to truncate all dependent tables together
  3. Truncating tables in the wrong order (child before parent) during a cleanup operation
  4. Forgetting that RESTRICT is the default behavior—explicitly requires CASCADE to proceed

How to fix "ERROR: cannot truncate a table referenced in a foreign key constraint"

1Check the error message to identify which table has the foreign key reference

The error will tell you which table cannot be truncated and which constraint is blocking it:

bash
ERROR: cannot truncate a table referenced in a foreign key constraint
DETAIL: Table "orders" references "customers" via foreign key "orders_customer_id_fkey".

This tells you:
- You're trying to TRUNCATE table customers
- Table orders has child records with foreign keys pointing to customers
- The constraint name is orders_customer_id_fkey

2Use CASCADE to truncate all dependent tables automatically

The simplest solution is to add CASCADE, which will truncate all dependent tables:

sql
-- Truncate the parent table and all child tables at once
TRUNCATE TABLE customers CASCADE;

This is equivalent to:

sql
TRUNCATE TABLE customers, orders CASCADE;

Note: CASCADE truncates ALL tables that reference the one you specified, not just direct children. Be careful you understand the full impact.

3Alternatively, truncate specific tables in dependency order

If you only want to truncate certain tables, truncate child tables BEFORE parent tables:

sql
-- Correct order: child (orders) BEFORE parent (customers)
TRUNCATE TABLE orders;
TRUNCATE TABLE customers;

This works because once the child table is empty, there are no foreign key references left.

For multiple levels of relationships, truncate in reverse dependency order:

sql
TRUNCATE TABLE order_items;  -- Deepest child
TRUNCATE TABLE orders;       -- Parent of order_items
TRUNCATE TABLE customers;    -- Parent of orders
4Use RESTART IDENTITY to reset sequence counters

When truncating tables with auto-incrementing ID columns, optionally reset the sequence:

sql
-- Truncate and reset ID counter to 1
TRUNCATE TABLE customers RESTART IDENTITY CASCADE;

Without RESTART IDENTITY:

sql
-- Just truncates, but next INSERT will continue from previous counter
TRUNCATE TABLE customers CASCADE;
-- If you deleted up to ID 1000, the next insert gets ID 1001

Use RESTART IDENTITY only if you want to reuse lower ID values.

5For bulk data cleanup: temporarily disable constraints

For large cleanup operations, you can disable constraints, truncate, then re-enable:

sql
-- Disable all triggers (including FK constraints)
ALTER TABLE customers DISABLE TRIGGER ALL;
ALTER TABLE orders DISABLE TRIGGER ALL;

-- Now truncate without restriction
TRUNCATE TABLE customers, orders;

-- Re-enable everything
ALTER TABLE customers ENABLE TRIGGER ALL;
ALTER TABLE orders ENABLE TRIGGER ALL;

Caution: Use only in controlled scenarios where you're sure referential integrity won't be violated.

6Within transactions for safety

Always wrap TRUNCATE operations in transactions so you can ROLLBACK if something goes wrong:

sql
BEGIN;
  TRUNCATE TABLE customers CASCADE;
  -- Verify the data you want is actually gone
  SELECT COUNT(*) FROM customers;  -- Should be 0
  -- Add more checks as needed
COMMIT;  -- Or ROLLBACK if something looks wrong

If you realize you made a mistake before COMMIT, ROLLBACK will undo the truncate.

Advanced notes

TRUNCATE vs DELETE Performance: TRUNCATE is much faster because it doesn't scan individual rows—it deallocates the table pages directly. For large tables, the difference is orders of magnitude. However, TRUNCATE doesn't fire ON DELETE triggers (only ON TRUNCATE triggers).

Sequence Behavior Without RESTART IDENTITY: If your table has a SERIAL or BIGSERIAL column (which uses a sequence), the sequence counter is NOT reset by TRUNCATE unless you use RESTART IDENTITY. This means after truncating, the next INSERT will get an ID one higher than the last deleted row's ID, leaving gaps.

CASCADE and Multiple Levels: When you use CASCADE, PostgreSQL truncates not just direct children, but all descendant tables. If you have A ← B ← C (C references B, B references A), then TRUNCATE A CASCADE will truncate all three. Be very careful with this in complex schemas.

Constraint Checking Inside Transactions: TRUNCATE constraints are always checked immediately, even if you SET CONSTRAINTS to DEFERRED. This is different from INSERT/UPDATE/DELETE constraints. If a table is referenced by a foreign key, CASCADE is required—there's no deferred alternative.

Frequently asked questions

What does "ERROR: cannot truncate a table referenced in a foreign key constraint" mean?
PostgreSQL's TRUNCATE statement is an optimized way to delete all rows from a table without scanning it individually. However, TRUNCATE prevents data integrity violations by refusing to truncate tables that are referenced by foreign keys from other tables. When PostgreSQL detects that you're trying to truncate a table with incoming foreign key references, it raises this error.
How do I fix "ERROR: cannot truncate a table referenced in a foreign key constraint"?
This error occurs when you try to TRUNCATE a table that is referenced by another table through a foreign key constraint. TRUNCATE is much faster than DELETE but cannot proceed if child tables have references. Use CASCADE to truncate all related tables together, truncate tables in dependency order, or temporarily disable constraints.
What causes "ERROR: cannot truncate a table referenced in a foreign key constraint"?
Attempting to TRUNCATE a table that has foreign key references coming FROM other tables (is a parent table) Not using the CASCADE option needed to truncate all dependent tables together Truncating tables in the wrong order (child before parent) during a cleanup operation
Is the "ERROR: cannot truncate a table referenced in a foreign key constraint" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading