Incident brief
Null value in column violates not-null constraint
A column was declared NOT NULL, and an INSERT or UPDATE tried to leave it null anyway. PostgreSQL rejects the row before it is ever stored.
What lands in your log
ERROR: null value in column "email" of relation "customers" violates not-null constraint
In 10 seconds
- What triggers it
- Create a table with a column marked NOT NULL.
- Fix
- Provide a real value for the column on every INSERT/UPDATE.
- Proof
- Reproduced on PostgreSQL 16.14 → The INSERT that left email NULL was rejected with SQLSTATE 23502 and the row was never stored, the table stayed empty.
Fix
What to do right now
Application-level steps for this error.
- Provide a real value for the column on every INSERT/UPDATE.
- If a value is genuinely optional, remove the NOT NULL constraint instead of forcing a placeholder.
- Do not rely on a column DEFAULT to fill in NULL, a DEFAULT of NULL is still NULL, and NOT NULL still rejects it.
-- Table already enforces NOT NULL. Provide a real value.
INSERT INTO billing.customers (id, email) VALUES (2, '[email protected]');
SELECT id, email FROM billing.customers 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.
Null value in column violates not-null constraint is explained by a stored integrity rule. Match the constraint name from PostgreSQL DETAIL to the catalog definition before changing data or code.
NOT NULL columns and defaults
Inspect the rejected table's required columns and whether a server default exists.
SELECT a.attrelid::regclass AS table_name, a.attname AS column_name,
a.attnotnull, pg_get_expr(d.adbin, d.adrelid) AS default_expr
FROM pg_attribute a
LEFT JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
WHERE a.attnum > 0 AND NOT a.attisdropped AND a.attnotnull
ORDER BY a.attrelid::regclass::text, a.attnum
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.2 Not-Null Constraints
A not-null constraint simply specifies that a column must not assume the null value.Read the full section on postgresql.org →
The insert that violates NOT NULL
PostgreSQL checks the NOT NULL constraint before the row is stored. The DETAIL line shows exactly which row was rejected and why, email was null, and that's not allowed.Checking the table afterward
The table is empty. The rejected INSERT never happened, there's no half-written row 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 column marked NOT NULL.
- 2Try to insert a row that leaves that column NULL.
- 3PostgreSQL rejects the row with SQLSTATE 23502 before it is stored.
One client: create a NOT NULL column, try to insert a NULL into it, then check the table.
CREATE SCHEMA IF NOT EXISTS billing;
DROP TABLE IF EXISTS billing.customers;
CREATE TABLE billing.customers (
id integer primary key,
email text NOT NULL
);INSERT INTO billing.customers (id, email) VALUES (1, NULL);SELECT * FROM billing.customers;What PostgreSQL actually returned
DROP TABLE
CREATE TABLEERROR: null value in column "email" of relation "customers" violates not-null constraint
DETAIL: Failing row contains (1, null). id | email
----+-------
(0 rows)A column declared NOT NULL DEFAULT NULL might look like it 'defaults to allowed', but the manual says the constraint applies even to values that come from a DEFAULT. This was tested directly.
Without this
Above: an explicit NULL is rejected by NOT NULL.
With this, tested
Below: omitting the column and relying on its own DEFAULT NULL is rejected the same way.
- A second operational test: exact SQL, raw output, measured result, and engineer notes
- Fix that looks like it works but only hides the value: 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.