SQLite error SQLITE_CONSTRAINT_FOREIGNKEY
FOREIGN KEY constraint failed
A row refers to a parent row that doesn’t exist, or you tried to delete or change a parent that other rows still refer to. SQLite checks this only when PRAGMA foreign_keys = ON, which is off on every new connection.
FOREIGN KEY constraint failed
Tested on SQLite 3.51.0 (macOS /usr/bin/sqlite3) · Updated 9 October 2026
What it means
A foreign key says a column in one table (the child, albums.artist_id) must match a row in
another (the parent, artists.id). Your statement would have broken that: a child pointing at a
parent that doesn’t exist, or a parent removed or renumbered while children still point at it.
Unlike most databases, SQLite doesn’t say which table, column or row. The message is always the
same, with the result code SQLITE_CONSTRAINT (19), extended code SQLITE_CONSTRAINT_FOREIGNKEY
(787).
Foreign keys are off by default. SQLite only enforces them on a connection that has run
PRAGMA foreign_keys = ON, and the setting lasts only for that connection. That’s why the same
statement can work in one tool and fail in your app (or the other way round), and why a database
with foreign keys declared can still contain rows that break them.
Common causes
- A child row with a parent id that doesn’t exist: a wrong id, or the child inserted before its parent.
- Deleting a parent that still has children, when the foreign key has no
ON DELETE CASCADEorON DELETE SET NULL. - Changing a parent’s key that children use.
- Dropping a parent table.
DROP TABLEdeletes its rows first, and that fails while children refer to them. - A deferred foreign key at
COMMIT. Constraints declaredDEFERRABLE INITIALLY DEFERREDare checked when the transaction commits, so the error comes fromCOMMIT, not from the statement that caused it.
How to fix it
Find the missing or blocking row
For an insert or update, check that the parent exists:
SELECT id FROM artists WHERE id = 98;
For a delete, find the children that still refer to the parent:
SELECT id, title FROM albums WHERE artist_id = 1;
To list every row that breaks a foreign key, including ones written while enforcement was off:
PRAGMA foreign_key_check;
albums|2|artists|0
Each line is the child table, the child’s rowid, the parent table, and the foreign key’s number.
Insert parents first, or defer the check
Insert the parent before the child, and delete children before their parent. When rows refer to each other or arrive in any order (an import, a copy between databases), defer the check to the end of the transaction:
BEGIN;
PRAGMA defer_foreign_keys = ON;
INSERT INTO albums VALUES (60, 'Later', 77); -- parent 77 doesn't exist yet
INSERT INTO artists VALUES (77, 'Late artist');
COMMIT;
defer_foreign_keys turns itself off when the transaction ends. If a row is still missing at
COMMIT, the commit fails and the transaction stays open, so you can fix the data and commit again.
Decide what deleting a parent should do
If children should go with their parent, or lose the link, say so in the table definition:
artist_id INTEGER REFERENCES artists (id) ON DELETE CASCADE
SQLite’s ALTER TABLE can’t change an existing foreign key. You rebuild the table: create a new
one with the right definition, copy the rows across, drop the old one and rename the new one, as
described in SQLite’s ALTER TABLE documentation.
Turn enforcement on, at the right moment
Run this right after opening each connection, outside any transaction:
PRAGMA foreign_keys = ON;
Inside a transaction it does nothing and reports no error. Check it took effect with
PRAGMA foreign_keys;, which returns 1. When you first turn it on for an old database, run
PRAGMA foreign_key_check; to find the rows that were written while it was off.
“foreign key mismatch” is a different problem
If the message is foreign key mismatch - "c3" referencing "p3", the foreign key itself is
broken: the parent column isn’t a primary key or unique, or doesn’t exist. Add a unique constraint
or index to the parent column, or point the foreign key at the right column.
Reproduce it
macOS /usr/bin/sqlite3, SQLite 3.51.0, with artists (id, name) and
albums (id, title, artist_id REFERENCES artists (id)), and one artist with one album. A new
connection has enforcement off, so an orphan goes in without complaint:
sqlite3 fk.db "PRAGMA foreign_keys;"
sqlite3 fk.db "INSERT INTO albums VALUES (2, 'Ghost', 99); SELECT changes();"
0
1
With enforcement on:
sqlite3 fk.db "PRAGMA foreign_keys = ON; INSERT INTO albums VALUES (3, 'Ghost 2', 98);"
Error: stepping, FOREIGN KEY constraint failed (19)
The shell adds “Error: stepping,” and the result code; SQLite’s message is
FOREIGN KEY constraint failed. Deleting the artist, changing its id and DROP TABLE artists
failed with the same message. Turning enforcement on inside a transaction was ignored:
sqlite3 fk.db "BEGIN; PRAGMA foreign_keys = ON; PRAGMA foreign_keys; COMMIT;"
0
A deferred foreign key failed at COMMIT (line 5 of the script), and succeeded once the parent was
inserted and the transaction committed again:
Runtime error near line 5: FOREIGN KEY constraint failed (19)
In Inlet
Inlet shows SQLite’s message when a statement or a commit fails. Run PRAGMA foreign_keys; in the
query editor to see whether the connection enforces foreign keys, and PRAGMA foreign_key_check;
to list orphan rows. Grid edits are staged until you commit (⌘S), and Review shows the exact SQL
first, so you can check the order of inserts and deletes before they run.