Fixing the 'Cannot Truncate Table' Foreign Key Error in PostgreSQL

intermediate🐘 PostgreSQL2026-07-24| PostgreSQL (all versions), any OS (Linux, macOS, Windows)

Error Message

ERROR: cannot truncate a table referenced in a foreign key constraint
#postgresql#sql#database-administration#foreign-key

Why This Error HappensYou tried to wipe a table clean with TRUNCATE, but PostgreSQL stopped you. This happens because another table relies on the data you're trying to delete through a foreign key constraint. Postgres is protecting you from creating 'orphaned' records—child rows that point to parent IDs that no longer exist.

Unlike the DELETE command, which scans every row to check for dependencies, TRUNCATE is a blunt instrument. It bypasses row-level checks and simply deallocates the data pages. Because it doesn't look at individual rows, it can't verify if a specific record is safe to remove. To maintain data integrity, Postgres blocks the operation entirely if any foreign key points to the table.

Solution 1: The CASCADE SledgehammerThe fastest fix is to append the CASCADE keyword. This tells PostgreSQL to automatically truncate any table that references your target table, and any tables referencing those, and so on.

TRUNCATE TABLE users CASCADE;

Be careful: this is recursive. If users is linked to orders, and orders is linked to order_items, all three tables will be wiped instantly. On a large database with 10+ dependent tables, one command can clear millions of rows across your entire schema.

Solution 2: Truncate Multiple Tables at OnceIf you want more control, list every related table in a single command. Postgres allows a TRUNCATE to proceed if all sides of the foreign key relationship are cleared at the exact same moment.

TRUNCATE TABLE users, orders, order_items;

This approach is often safer than CASCADE. It forces you to acknowledge exactly which tables are being emptied, preventing you from accidentally nuking data in a table you forgot was linked.

Solution 3: The 'Replica' Role Trick (Power Users Only)Sometimes you need to clear a parent table while keeping the child tables intact—perhaps during a complex data migration or when re-seeding specific IDs. You can bypass constraint checks by changing the session_replication_role to replica.

BEGIN;
-- Temporarily ignore foreign keys
SET LOCAL session_replication_role = 'replica';

TRUNCATE TABLE users;

-- Return to normal behavior
SET LOCAL session_replication_role = 'origin';
COMMIT;

Use this with caution. If you don't immediately repopulate the users table with the correct IDs, your database will be in an inconsistent state. Your application will likely crash when it tries to join orders to users and finds nothing.

Solution 4: Use DELETE for Smaller DatasetsIf your table is relatively small—say, under 100,000 rows—you might not need TRUNCATE at all. A standard DELETE statement respects foreign key constraints and triggers.

DELETE FROM users;

While TRUNCATE takes milliseconds regardless of table size, DELETE performance scales with the number of rows. On a table with 5 million rows, DELETE might take 30 seconds and bloat your transaction log, whereas TRUNCATE would be nearly instantaneous.

How to Identify Dependent TablesBefore you run a CASCADE, it helps to know which tables are actually standing in your way. Run this query to find every table that currently references your target:

SELECT 
    rel_tco.table_name AS referencing_table, 
    rel_tco.constraint_name
FROM 
    information_schema.referential_constraints rco
JOIN 
    information_schema.table_constraints rel_tco 
    ON rco.constraint_name = rel_tco.constraint_name
JOIN 
    information_schema.table_constraints pk_tco 
    ON rco.unique_constraint_name = pk_tco.constraint_name
WHERE 
    pk_tco.table_name = 'users';

Summary of Best Practices- In Development: TRUNCATE ... CASCADE is usually fine for resetting local environments.- In Production: Avoid CASCADE unless you have a full map of your database dependencies. Use explicit multi-table truncation instead.- For Large Wipes: If you're clearing millions of rows, stick with TRUNCATE. DELETE will likely be too slow and may lock the table for an extended period.

Related Error Notes