Incident brief
RESTRICT foreign-key violation
Deleting or updating a parent row still referenced by a child fails when the foreign key is ON DELETE/UPDATE RESTRICT. On PostgreSQL 18+, that path raises SQLSTATE 23001 (restrict_violation) with a RESTRICT-specific message. On PostgreSQL 14-17 the same RESTRICT action still blocks the change but reports SQLSTATE 23503 (foreign_key_violation), same practical fix, different errcode. Clear or reassign the child rows first.
What lands in your log
ERROR: update or delete on table "parent_r" violates RESTRICT setting of foreign key constraint "child_r_pid_fkey" on table "child_r"
In 10 seconds
- What triggers it
- Create a parent/child foreign key with ON DELETE RESTRICT and insert a referenced parent row plus a child that references it.
- Fix
- Delete or reassign the referencing child rows before deleting the parent.
- Proof
- Reproduced on PostgreSQL 18.6 (primary); cross-checked 16.14 → On PG 18.6, DELETE parent under ON DELETE RESTRICT raises SQLSTATE 23001 with RESTRICT-specific message text. On PG 16.14 the identical DDL/DML raises SQLSTATE 23503 instead, still blocked, different code.
Fix
What to do right now
Application-level steps for this error.
- Delete or reassign the referencing child rows before deleting the parent.
- Or, if cascade semantics are intended, define the foreign key ON DELETE CASCADE deliberately.
- Version note: only PostgreSQL 18+ distinguishes RESTRICT as SQLSTATE 23001; on 14-17 match on the FK violation message/constraint name or treat 23503 the same way operationally.
-- Remove the referencing child rows first.
DELETE FROM child_r WHERE pid = 1;
DELETE FROM parent_r WHERE id = 1;For this error
See this error live on the server
Run these against the affected instance to confirm the diagnosis before you act.
Inspect the foreign key's ON DELETE/UPDATE action and confirm dependent rows. PostgreSQL 16 commonly reports 23503 for this case; 23001 behavior is version-sensitive.
RESTRICT/NO ACTION foreign keys
confdeltype/confupdtype r means RESTRICT and a means NO ACTION.
SELECT conname, conrelid::regclass AS child_table,
confrelid::regclass AS parent_table,
confdeltype, confupdtype,
pg_get_constraintdef(oid) AS definition
FROM pg_constraint
WHERE contype = 'f' AND (confdeltype = 'r' OR confupdtype = 'r')
ORDER BY conrelid::regclass::text, conname;Server version for SQLSTATE interpretation
Record major version with the incident because the emitted code differs across supported releases.
SELECT current_setting('server_version') AS server_version;Why it happens
What PostgreSQL is telling you
The mechanism behind the error, grounded in the official manual, not paraphrased.
PostgreSQL 18 Documentation, Appendix A. PostgreSQL Error Codes (Table A.1, Class 23, Integrity Constraint Violation)
23001 → restrict_violationRead the full section on postgresql.org →
Deleting a referenced parent under RESTRICT
The child still references the parent key, so RESTRICT raises 'update or delete on table parent_r violates RESTRICT setting of foreign key constraint child_r_pid_fkey'.The session continues normally
The RESTRICT check failed outside a transaction, so the session has nothing to undo before its next query.Reproduce & verify
A real, single-session PostgreSQL reproduction
A literal transcript of SQL run against a live PostgreSQL instance in an isolated lab. The commands below are exactly what was executed.
- 1Create a parent/child foreign key with ON DELETE RESTRICT and insert a referenced parent row plus a child that references it.
- 2DELETE the parent while the child row still references it.
- 3PostgreSQL 18+: aborts with SQLSTATE 23001 and message text mentioning the RESTRICT setting. PostgreSQL 14-17: same block with SQLSTATE 23503 (foreign_key_violation).
A cleanup deleted a parent record while child rows still pointed at it, and the RESTRICT foreign key refused the delete.
CREATE TABLE parent_r(id int primary key);
CREATE TABLE child_r(id int, pid int REFERENCES parent_r(id) ON DELETE RESTRICT);
INSERT INTO parent_r VALUES (1);
INSERT INTO child_r VALUES (10, 1);DELETE FROM parent_r WHERE id = 1;SELECT 'ok' AS session_after_error;What PostgreSQL actually returned
CREATE TABLE
CREATE TABLE
INSERT 0 1
INSERT 0 1ERROR: update or delete on table "parent_r" violates RESTRICT setting of foreign key constraint "child_r_pid_fkey" on table "child_r"
DETAIL: Key (id)=(1) is referenced from table "child_r". session_after_error
---------------------
ok
(1 row)The parent deletes once its referencing children are gone.
Without this
Before: deleting the parent is blocked
With this, tested
After: children removed, parent deletes
- A second operational test: exact SQL, raw output, measured result, and engineer notes
Card required. Cancel before day 7 and you are not charged.
Runbook to fix this
The runbook for this incident
Full step-by-step fixes for the condition behind this error: the diagnosis, the exact SQL, and output captured in the lab.
Connected
Everything this error touches
Every page this SQLSTATE connects to: the concept that explains it, the runbooks that fix it, the parameters you tune to prevent it, and the sibling errors it travels with. All real cross-references. Jump straight in, or open the full interactive map.
Verification
- Last verified
- 2026-08-15 (Docker lab, PostgreSQL 18.6 + 16.14)
- Verification scope
- Verified against PostgreSQL 18.6 (23001) and 16.14 (23503 for same RESTRICT DDL) in isolated labs
- Audit status
- reviewed
Went further?
Pro unlocks the second lab proof
Free page stops the bleeding. Pro adds the operational test, SQLSTATE audit, and deeper evidence, same error, more certainty.