Skip to content
SQL errors/Fix “NOT NULL Constraint Failed” in SQL
SQL error

Fix “NOT NULL Constraint Failed” in SQL

A required column got no value. Supply one, give the column a default, or let it be null.

The symptom

An INSERT or UPDATE is rejected because a column declaredNOT NULL would end up empty. The whole statement fails; it does not insert the other columns and leave that one blank.

The garden_shop practice tables load straight from CSV and carry no constraints of their own, so the bench below declares a small table with aNOT NULL rule to fail against.

DuckDB names the exact column: Constraint Error: NOT NULL constraint failed: order_imports.customer_email. That is the most useful part of the message. You do not have to guess which column was empty, and on a wide insert that saves real time.

Three ways a value goes missing

The obvious one is an explicit null. The second is easier to miss: leaving a column out of the column list means it takes its default, and a column with no default defaults to null. Neither statement below contains a value you would call wrong.

An explicit null
create or replace table order_imports (
  order_id integer,
  customer_email varchar not null
);

insert into order_imports values (1, null);
A column left out of the list
-- customer_email is never named, so it defaults to null
insert into order_imports (order_id)
values (2);

The third is a source column that is null in more rows than you expected.orders.coupon_code is populated on 6 of Garden Shop's 24 orders, so selecting it into a NOT NULL column fails on the first null it reaches. A LEFT JOIN that finds no match does the same thing for the same reason, though Garden Shop happens to have no orphaned rows to demonstrate it with.

A source column that is mostly null
insert into order_imports
select order_id, coupon_code
from orders;

Count the offending rows first

Before changing anything, count what would fail. "18 rows of 24" is a data problem to think about. "Every row" usually means the wrong column or the wrong join.

How many rows would be rejected
select count(*) as total_rows,
       count(*) - count(coupon_code) as rows_without_coupon
from orders

Pick the right fix

There are three, and they are not interchangeable. Supply the value when the data exists somewhere. Give the column a DEFAULT when a sensible placeholder exists and the rule should stay. Drop the NOT NULL rule only when the column is genuinely optional, which is a decision about the model rather than about this one insert.

Substitute a value with COALESCE
insert into order_imports
select order_id,
       coalesce(coupon_code, 'NONE')
from orders;

select count(*) as inserted
from order_imports;
Or let the column default
create or replace table order_imports (
  order_id integer,
  customer_email varchar not null default 'unknown@example.com'
);

insert into order_imports (order_id)
values (1);

select *
from order_imports;

A DEFAULT and a NOT NULL rule work together rather than against each other: the rule still rejects an explicit null, and the default only covers the case where the column was not mentioned at all.

Watch out

Filling nulls with a placeholder makes the insert succeed and hides the question. 'NONE' in 18 of 24 rows is defensible; the same trick with'unknown@example.com' across 4,000 rows is worse than 4,000 rejected rows, because nothing will fail again to remind you. Substitute a value only when the placeholder means something to whoever reads the table.

How other engines word it

EngineMessage
DuckDBConstraint Error: NOT NULL constraint failed: order_imports.customer_email
PostgreSQLERROR: null value in column "customer_email" of relation "order_imports" violates not-null constraint
MySQLColumn 'customer_email' cannot be null
SQL ServerCannot insert the value NULL into column 'customer_email'; column does not allow nulls. INSERT fails.
SQLiteNOT NULL constraint failed: order_imports.customer_email

Learn more