DEV Community

umzzil nng
umzzil nng

Posted on Originally published at oraerror.com

PostgreSQL 23000 Error: Causes and Solutions Complete Guide

PostgreSQL Error 23000: Integrity Constraint Violation

PostgreSQL error code 23000 is a top-level error class representing an integrity constraint violation, thrown whenever an operation attempts to insert, update, or delete data that breaks a rule defined on the database schema. In practice, you will almost always see a more specific child error code (such as 23503 or 23505) alongside it, but understanding the parent class is essential for robust error handling in your application.


Top 3 Causes

1. Foreign Key Violation (23503)

Inserting a child record that references a non-existent parent, or deleting a parent record that still has dependent children, triggers this error. It is the single most common cause in production systems, especially during bulk data migrations where insertion order is wrong.

-- This fails if customer_id 999 does not exist in customers table
INSERT INTO orders (customer_id, product, amount)
VALUES (999, 'Laptop', 1500.00);
-- ERROR: insert or update on table "orders" violates foreign key constraint
-- DETAIL: Key (customer_id)=(999) is not present in table "customers".

-- Fix: Insert parent record first, then the child
BEGIN;
  INSERT INTO customers (id, name) VALUES (999, 'John Doe')
  ON CONFLICT (id) DO NOTHING;

  INSERT INTO orders (customer_id, product, amount)
  VALUES (999, 'Laptop', 1500.00);
COMMIT;

-- Find all orphaned child records
SELECT o.*
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.id
WHERE c.id IS NULL;
Enter fullscreen mode Exit fullscreen mode

2. Unique Constraint Violation (23505)

Attempting to insert a duplicate value into a column with a UNIQUE or PRIMARY KEY constraint causes this error. It is especially problematic in high-concurrency environments where race conditions can cause two transactions to attempt the same insert simultaneously.

-- This fails if the email already exists
INSERT INTO users (email, username)
VALUES ('alice@example.com', 'alice');
-- ERROR: duplicate key value violates unique constraint "users_email_key"

-- Fix: Use INSERT ... ON CONFLICT (Upsert)
INSERT INTO users (email, username, updated_at)
VALUES ('alice@example.com', 'alice_new', NOW())
ON CONFLICT (email)
DO UPDATE SET
  username = EXCLUDED.username,
  updated_at = EXCLUDED.updated_at;

-- Find all duplicate emails before enforcing a unique constraint
SELECT email, COUNT(*) AS duplicates
FROM users
GROUP BY email
HAVING COUNT(*) > 1
ORDER BY duplicates DESC;
Enter fullscreen mode Exit fullscreen mode

3. NOT NULL and CHECK Constraint Violation (23502 / 23514)

Inserting a NULL into a NOT NULL column, or providing a value that fails a CHECK expression, raises these errors. They commonly appear when migrating data from legacy systems with poor data quality or when application-level validation is skipped.

-- NOT NULL violation
INSERT INTO employees (id, name, department_id)
VALUES (1, 'Bob', NULL);
-- ERROR: null value in column "department_id" violates not-null constraint

-- CHECK violation
INSERT INTO products (name, price)
VALUES ('Widget', -50.00);
-- ERROR: new row for relation "products" violates check constraint "chk_price_positive"

-- Fix: Use COALESCE and GREATEST to sanitize input
INSERT INTO employees (id, name, department_id, salary)
VALUES (
  1,
  'Bob',
  COALESCE(NULL, 1),         -- fallback to default department
  GREATEST(COALESCE(NULL, 0), 0)  -- ensure non-negative salary
);

-- Inspect existing CHECK constraints on a table
SELECT conname, pg_get_constraintdef(oid) AS definition
FROM pg_constraint
WHERE conrelid = 'products'::regclass
  AND contype = 'c';
Enter fullscreen mode Exit fullscreen mode

Quick Fix Solutions

-- Temporarily disable triggers for bulk migration (re-enable immediately after!)
ALTER TABLE orders DISABLE TRIGGER ALL;
-- ... bulk load data ...
ALTER TABLE orders ENABLE TRIGGER ALL;

-- Validate a constraint after fixing data
ALTER TABLE orders VALIDATE CONSTRAINT fk_orders_customer;

-- Catch constraint violations gracefully in PL/pgSQL
DO $$
BEGIN
  INSERT INTO orders (customer_id, product, amount)
  VALUES (999, 'Laptop', 1500.00);
EXCEPTION
  WHEN foreign_key_violation THEN
    RAISE NOTICE 'Parent record does not exist. Skipping insert.';
  WHEN unique_violation THEN
    RAISE NOTICE 'Duplicate record detected. Skipping insert.';
  WHEN integrity_constraint_violation THEN
    RAISE NOTICE 'Generic constraint violation caught.';
END;
$$;
Enter fullscreen mode Exit fullscreen mode

Prevention Tips

1. Always validate data before it hits the database.
Apply input validation at the application layer and use ON CONFLICT clauses for idempotent inserts. Never rely solely on the database to catch bad data — catching it earlier is cheaper and produces better user-facing error messages.

2. Monitor and log constraint violations proactively.
Configure log_min_error_statement = 'error' in postgresql.conf and integrate with tools like pgBadger or Prometheus to detect recurring violation patterns before they become incidents. Set up alerts when 23xxx error codes spike in your log pipeline.


Related Error Codes

Code Name Description
23001 restrict_violation RESTRICT rule triggered on delete/update
23502 not_null_violation NULL inserted into NOT NULL column
23503 foreign_key_violation Referenced parent key does not exist
23505 unique_violation Duplicate value in UNIQUE/PK column
23514 check_violation Value fails a CHECK constraint
23P01 exclusion_violation EXCLUDE constraint conflict (e.g., overlapping ranges)

📖 Want a more detailed guide?
Check out the full in-depth version (Korean) on oraerror.com — includes detailed analysis, additional SQL examples, and prevention tips.

Top comments (0)