ERROR 1048 (23000): Column 'column_name' cannot be null is raised when an INSERT or UPDATE statement attempts to store a NULL value in a column that has a NOT NULL constraint. The error symbol is ER_BAD_NULL_ERROR.
Impact
The statement that triggered the error is rolled back immediately. In an autocommit session this means only that single statement fails; inside an explicit transaction the transaction remains open but the failing statement is aborted, requiring application code to either retry or roll back the whole transaction.
This error surfaces frequently when schema changes (adding a NOT NULL column) are made without updating the application, when ORM models omit a required field, or when user input is passed through to a query without a nil/null guard. In frameworks like Laravel (Eloquent), Django, or Hibernate you will typically see a database exception wrapping the 1048 code, such as Illuminate\Database\QueryException, django.db.utils.IntegrityError, or org.hibernate.exception.ConstraintViolationException.
Common Causes
Missing column in INSERT: An
INSERTstatement that omits a column with noDEFAULTvalue and aNOT NULLconstraint — MySQL cannot supply any value for it.Explicit NULL in INSERT or UPDATE: The application or query explicitly passes
NULLfor aNOT NULLcolumn, e.g.INSERT INTO users (email) VALUES (NULL).ORM model missing a required field: The ORM is asked to save a record without populating a field that maps to a
NOT NULLcolumn, and the model does not define a fallback default.Schema migration added a NOT NULL column: A column was added to an existing table as
NOT NULLwithout aDEFAULT. Existing application code that buildsINSERTstatements without listing all columns now fails because the new column receives no value.SQL mode — STRICT mode disabled on older setup: On MySQL 5.6 and earlier with
STRICT_TRANS_TABLESorSTRICT_ALL_TABLESdisabled, MySQL would silently coerce NULL to the column's implicit zero-value instead of raising 1048. After upgrading or enabling strict mode the error starts appearing on previously silent queries.Trigger or generated column side-effect: A
BEFORE INSERTtrigger sets a column to NULL, or a virtual column expression evaluates to NULL for a row that violates the constraint.
Troubleshooting and Resolution Steps
Identify which column is NULL. The error message names the column. Verify the column's definition:
DESCRIBE orders; -- or SHOW CREATE TABLE orders\GLook for
NOT NULLin the column definition and whether aDEFAULTvalue is present.Check whether the column has a DEFAULT. If the column should have a sensible default (e.g., a timestamp, a status enum, or a zero counter), add one without changing the constraint:
ALTER TABLE orders ALTER COLUMN status SET DEFAULT 'pending';Supply the missing value in the query. If the column must contain meaningful application data, update the
INSERTorUPDATEstatement to provide it:-- Before (broken) INSERT INTO users (username) VALUES ('alice'); -- After (fixed — provide the required email value) INSERT INTO users (username, email) VALUES ('alice', 'alice@example.com');Remove the NOT NULL constraint if NULL is a valid business state. If the column genuinely should allow absence of data, relax the constraint:
ALTER TABLE orders MODIFY COLUMN shipped_at DATETIME NULL;Add a DEFAULT for a newly added column to fix existing app code. When a migration added a
NOT NULLcolumn and you cannot update all insert sites immediately, provide a default:ALTER TABLE products ADD COLUMN weight_kg DECIMAL(8,2) NOT NULL DEFAULT 0.00;Use INFORMATION_SCHEMA to find all NOT NULL columns without defaults (useful after a migration audit):
SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'your_database' AND IS_NULLABLE = 'NO' AND COLUMN_DEFAULT IS NULL AND EXTRA NOT LIKE '%auto_increment%';Debug ORM-level nulls. Enable query logging or use your ORM's debug output to capture the raw SQL being sent and confirm which parameter is NULL:
SET GLOBAL general_log = 'ON'; SET GLOBAL general_log_file = '/tmp/mysql_general.log';Remember to turn it off again after debugging in production.
Additional Information
- SQLSTATE 23000 is the "integrity constraint violation" class. Error 1048 shares this class with 1062 (ER_DUP_ENTRY) and 1452 (ER_NO_REFERENCED_ROW_2).
- Strict SQL mode: With
STRICT_TRANS_TABLES(the default since MySQL 5.7.5 and in MySQL 8.0), inserting NULL into aNOT NULLcolumn without a default always raises 1048. With strict mode disabled, MySQL 5.6 and earlier would silently insert the column's implicit default (0, '', or the epoch datetime). Enabling strict mode on an older deployment can expose latent 1048 violations. - AUTO_INCREMENT columns:
AUTO_INCREMENTprimary key columns are exempt — passing NULL or omitting them causes MySQL to generate the next value, not raise 1048. - ENUM columns: Inserting NULL into a
NOT NULL ENUMcolumn raises 1048 even though ENUM has an implicit empty-string member; the constraint is evaluated before the ENUM coercion. - Bulk loads (LOAD DATA INFILE): Missing fields in a CSV map to NULL; a
NOT NULLcolumn without a default will trigger 1048 for each such row unlessIGNOREis specified (which silently skips the rows in non-strict mode).
Frequently Asked Questions
Why does the same INSERT work in development but fail in production?
The most common reason is a difference in sql_mode. Development databases are often created with older defaults that omit STRICT_TRANS_TABLES, while production has it enabled. Run SELECT @@sql_mode; on both servers to compare. The fix is to correct the query rather than disable strict mode.
Can I set a NULL default on a NOT NULL column?
No — DEFAULT NULL is incompatible with NOT NULL. If you define NOT NULL DEFAULT NULL MySQL will raise an error. You must either supply a non-null default or drop the NOT NULL constraint.
My ORM is sending NULL for a column I populated. Why?
The ORM may be mapping the in-memory field to a different column name, or the value was set to None/null before the save call due to a type coercion (e.g., an empty string converted to null). Enable ORM-level debug logging to capture the bound parameters and trace back which property is null at save time.
Does REPLACE INTO behave differently than INSERT for this error?
No. REPLACE INTO performs a delete-then-insert internally; the insert step is subject to the same NOT NULL checks, so it will raise 1048 on a missing required column just as a plain INSERT would.