PostgreSQL error 23502
null value in column violates not-null constraint
A row would have NULL in a column declared NOT NULL. Either the statement left the column out and it has no default, or something sent an explicit NULL, which PostgreSQL stores as NULL even when the column has a default.
ERROR: null value in column "email" of relation "customers" violates not-null constraint
Tested on PostgreSQL 18.6 (also 14–17) · Updated 9 October 2026
What it means
The column is declared NOT NULL and the row you’re writing has no value in it. The statement is
rejected and none of its rows are written. The DETAIL line shows the whole row as it would have been
stored, in the table’s column order, with null where the value is missing:
ERROR: null value in column "email" of relation "customers" violates not-null constraint
DETAIL: Failing row contains (1, null, active, 2026-10-09 10:13:19.77362+00).
Columns you didn’t supply already show their defaults, so you see the row exactly as the server built it.
Common causes
- The column was left out of the
INSERTand has noDEFAULT. - An explicit
NULLwas sent. A default only applies when the column is omitted or you writeDEFAULT. Some ORMs and drivers send every column, withNULLfor anything unset, so the default never applies. - An
UPDATEset it toNULL, often through a form field left empty, or a subquery that found nothing. INSERT … SELECTfrom data with gaps: a staging table, aLEFT JOINthat didn’t match.- An empty field in a CSV file. With
COPY … (FORMAT csv), an unquoted empty field isNULL. - A
NULLfor an identity orserialcolumn. WritingNULLexplicitly intoiddoesn’t trigger the sequence; leave the column out instead.
Adding NOT NULL to an existing column that has nulls gives a different message with the same code,
column "email" of relation "staging" contains null values. See
setting a column NOT NULL.
How to fix it
Supply the value, or let the default apply
Leave the column out, or write DEFAULT for it, so its default is used:
INSERT INTO customers (email) VALUES ('ada@example.com');
INSERT INTO customers (email, status) VALUES ('grace@example.com', DEFAULT);
If the application sends NULL for unset fields, change it to omit them, or to send the value you
want stored.
Give the column a default
If there’s a sensible value for rows that don’t say, add a default. It applies to new rows only:
ALTER TABLE customers ALTER COLUMN status SET DEFAULT 'active';
Replace nulls in INSERT … SELECT
Filter them out, or replace them with coalesce:
INSERT INTO customers (email, status)
SELECT email, coalesce(status, 'active')
FROM staging
WHERE email IS NOT NULL;
Find the bad lines in a CSV
COPY names the line it stopped on in the CONTEXT line. Fix the file, or load it into a staging
table with no constraints first and clean it up with SQL. The ON_ERROR ignore option of PostgreSQL
17 and later doesn’t help here: it skips rows whose values can’t be converted to the column’s type, not
rows that break a constraint.
Ask whether the column should allow nulls
If “unknown” is a real state for this value, the constraint is wrong rather than the data:
ALTER TABLE customers ALTER COLUMN phone DROP NOT NULL;
Reproduce it
On PostgreSQL 18.6:
CREATE TABLE customers (
id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
email text NOT NULL,
status text NOT NULL DEFAULT 'active',
created_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO customers (status) VALUES ('active');
INSERT INTO customers (email, status) VALUES ('ada@example.com', NULL);
ERROR: null value in column "email" of relation "customers" violates not-null constraint
DETAIL: Failing row contains (1, null, active, 2026-10-09 10:13:19.77362+00).
ERROR: null value in column "status" of relation "customers" violates not-null constraint
DETAIL: Failing row contains (2, ada@example.com, null, 2026-10-09 10:13:19.77362+00).
The second insert sent an explicit NULL, so the 'active' default didn’t apply. Leaving status
out, or writing DEFAULT, stores active. Each failed insert still used up an identity value (1,
then 2). An explicit NULL for the identity column:
ERROR: null value in column "id" of relation "customers" violates not-null constraint
DETAIL: Failing row contains (null, linus@example.com, active, 2026-10-09 10:13:19.77362+00).
A CSV with an empty email on its second line, loaded into a fresh copy of the table:
COPY customers (email, status) FROM STDIN WITH (FORMAT csv);
bob@example.com,active
,active
\.
ERROR: null value in column "email" of relation "customers" violates not-null constraint
DETAIL: Failing row contains (2, null, active, 2026-10-09 10:22:36.61782+00).
CONTEXT: COPY customers, line 2: ",active"
COPY is all or nothing: the good first line isn’t kept either. A serial column behaves like the
identity column: INSERT INTO s (id, x) VALUES (NULL, 'a') fails with
null value in column "id" of relation "s". PostgreSQL 14, 15, 16 and 17 print the same messages,
including of relation "customers".
In Inlet
In the grid, Set NULL is an explicit action, and edits are staged until you commit (⌘S), with Review showing the exact SQL before anything is sent. In the query editor the error appears at the position the server reports, and with your own Anthropic API key Ask Claude (⌘L) can fix the failed statement, sending the schema, the SQL and the error, never rows.