Incident brief
New row violates check constraint
A column or table has a CHECK constraint, and an INSERT or UPDATE produced a row where that check evaluated to false. PostgreSQL rejects the row.
What lands in your log
ERROR: new row for relation "products" violates check constraint "products_price_check"
In 10 seconds
- What triggers it
- Create a table with a CHECK constraint, for example price > 0.
- Fix
- Fix the value so it satisfies the check before inserting.
- Proof
- Reproduced on PostgreSQL 16.14 → The INSERT with price = -10 was rejected with SQLSTATE 23514 because -10 > 0 is false, and the row was never stored.
Fix
What to do right now
Application-level steps for this error.
- Fix the value so it satisfies the check before inserting.
- If the rule was wrong, ALTER the constraint rather than working around it in application code.
- Remember a CHECK constraint passes on NULL, pair it with NOT NULL if NULL should also be rejected.
-- Table already has the CHECK from setup. Insert a row that satisfies it.
INSERT INTO catalog.products (id, price) VALUES (2, 19.99);
SELECT id, price FROM catalog.products ORDER BY id;For this error
See this error live on the server
Run these against the affected instance to confirm the diagnosis before you act.
New row violates check constraint is explained by a stored integrity rule. Match the constraint name from PostgreSQL DETAIL to the catalog definition before changing data or code.
CHECK expression that rejected the row
Read the exact CHECK expression and validation state for the constraint named in the error.
SELECT conname, conrelid::regclass AS table_name,
pg_get_constraintdef(oid) AS definition, convalidated
FROM pg_constraint
WHERE contype = 'c'
ORDER BY conrelid::regclass::text, conname
LIMIT 100;Why it happens
What PostgreSQL is telling you
The mechanism behind the error, grounded in the official manual, not paraphrased.
PostgreSQL 16 Documentation, §5.4.1 Check Constraints
It should be noted that a check constraint is satisfied if the check expression evaluates to true or the null value.Read the full section on postgresql.org →
The insert that violates the check
price > 0 evaluates to false for -10, so the CHECK constraint rejects the row before it's stored. The DETAIL line shows exactly which row failed.Checking the table afterward
The table is empty. The rejected insert left nothing behind to clean up.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 table with a CHECK constraint, for example price > 0.
- 2Try to insert a row that fails the check, for example a negative price.
- 3PostgreSQL rejects the row with SQLSTATE 23514.
One client: create a table with a CHECK (price > 0), insert a negative price, then check the table.
CREATE SCHEMA IF NOT EXISTS catalog;
DROP TABLE IF EXISTS catalog.products;
CREATE TABLE catalog.products (
id integer primary key,
price numeric CHECK (price > 0)
);INSERT INTO catalog.products (id, price) VALUES (1, -10);SELECT * FROM catalog.products;What PostgreSQL actually returned
DROP TABLE
CREATE TABLEERROR: new row for relation "products" violates check constraint "products_price_check"
DETAIL: Failing row contains (1, -10). id | price
----+-------
(0 rows)The manual's own wording, a check constraint passes on true OR null, was tested directly: a NULL price was inserted into the same price > 0 column and it went through with no error at all.
Without this
Above: a negative price is rejected by the check.
With this, tested
Below: a NULL price passes the same check, because the comparison itself evaluates to null, not false.
- A second operational test: exact SQL, raw output, measured result, and engineer notes
- Fix that looks safe but leaves old bad data behind: exact SQL, output, and verdict
- A manual-grounded production interpretation of the lab result
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.
Understand the concept
Fix it — runbooks
Related errors
Verification
- Last verified
- 2026-07-15 (Docker lab, PostgreSQL 16.14)
- Verification scope
- Verified against PostgreSQL 16.14 in an isolated lab environment
- 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.