This error occurs when an insert, update, or delete operation violates a foreign key relationship between tables. Foreign key constraints must be explicitly enabled in SQLite and violations happen when referenced data is missing or incorrectly modified.
The SQLITE_CONSTRAINT_FOREIGNKEY error indicates a violation of referential integrity between related tables. SQLite enforces foreign key constraints to ensure that relationships between tables remain consistent. This error occurs when you attempt to insert a row with a foreign key value that doesn't exist in the referenced parent table, update a foreign key to point to a non-existent record, or delete a parent record that still has dependent child records (when ON DELETE is set to RESTRICT or not specified). A critical detail about SQLite: foreign key constraints are disabled by default for backwards compatibility. You must explicitly enable them for each database connection using PRAGMA foreign_keys = ON. If foreign keys aren't enabled, SQLite silently ignores constraint violations, which can lead to data integrity issues.
Foreign keys must be enabled for each database connection. Add this immediately after opening the connection:
PRAGMA foreign_keys = ON;In Node.js with better-sqlite3:
const Database = require('better-sqlite3');
const db = new Database('mydb.sqlite');
db.pragma('foreign_keys = ON');In Python with sqlite3:
import sqlite3
conn = sqlite3.connect('mydb.sqlite')
conn.execute('PRAGMA foreign_keys = ON')Verify it's enabled:
PRAGMA foreign_keys; -- Should return 1Use the foreign_key_check pragma to identify violations:
PRAGMA foreign_key_check;This returns rows showing which foreign keys are invalid. For a specific table:
PRAGMA foreign_key_check(table_name);The output shows the child table, row ID, parent table, and which constraint failed.
Always create parent records before attempting to insert child records:
-- CORRECT: Parent first, then child
INSERT INTO authors (id, name) VALUES (1, 'Jane Doe');
INSERT INTO books (id, author_id, title)
VALUES (1, 1, 'My Book');
-- WRONG: Child before parent causes error
INSERT INTO books (id, author_id, title)
VALUES (1, 999, 'My Book'); -- author_id 999 doesn't exist!In transactions, maintain the correct order:
BEGIN TRANSACTION;
INSERT INTO authors (id, name) VALUES (1, 'Jane Doe');
INSERT INTO books (id, author_id, title) VALUES (1, 1, 'My Book');
COMMIT;When deleting related records, remove children first:
-- CORRECT: Delete children first
DELETE FROM books WHERE author_id = 1;
DELETE FROM authors WHERE id = 1;
-- WRONG: Deleting parent first causes error if children exist
DELETE FROM authors WHERE id = 1; -- Fails if books reference this author!Or use CASCADE to automate this:
CREATE TABLE books (
id INTEGER PRIMARY KEY,
author_id INTEGER,
title TEXT,
FOREIGN KEY (author_id) REFERENCES authors(id)
ON DELETE CASCADE -- Automatically delete child books
);Before inserting or updating, verify the foreign key value exists:
-- Check if author exists before inserting book
SELECT id FROM authors WHERE id = 5;
-- Only insert if author exists
INSERT INTO books (author_id, title)
SELECT 5, 'New Book'
WHERE EXISTS (SELECT 1 FROM authors WHERE id = 5);Or use a JOIN to ensure valid references:
-- Update only if new author_id exists
UPDATE books
SET author_id = 10
WHERE id = 1
AND EXISTS (SELECT 1 FROM authors WHERE id = 10);Deferred Foreign Key Constraints
Use PRAGMA defer_foreign_keys = ON to delay constraint checking until COMMIT:
BEGIN TRANSACTION;
PRAGMA defer_foreign_keys = ON;
-- Can temporarily violate constraints here
DELETE FROM authors WHERE id = 1;
DELETE FROM books WHERE author_id = 1;
-- Constraints checked at COMMIT
COMMIT;This is useful for circular dependencies or complex multi-table operations.
NULL Foreign Keys
NULL values in foreign key columns are always allowed (they don't reference anything):
INSERT INTO books (author_id, title) VALUES (NULL, 'Anonymous Book'); -- OKComposite Foreign Keys
Foreign keys can span multiple columns:
CREATE TABLE order_items (
order_id INTEGER,
product_id INTEGER,
FOREIGN KEY (order_id, product_id)
REFERENCES order_products(order_id, product_id)
);All columns in the composite key must match existing values in the parent table.
Foreign Key Actions
Define what happens when parent records change:
- ON DELETE CASCADE: Automatically delete child records
- ON DELETE SET NULL: Set foreign key to NULL in children
- ON DELETE RESTRICT: Prevent deletion (default)
- ON UPDATE CASCADE: Update foreign key when parent key changes
- ON UPDATE SET NULL: Set foreign key to NULL when parent changes
Why Foreign Keys Are Disabled by Default
SQLite disables foreign keys by default to maintain backwards compatibility with databases created before foreign key support was added (SQLite 3.6.19, 2009). This means legacy applications continue working, but new applications must explicitly opt in to constraint enforcement.
Performance Considerations
Foreign key checks add minimal overhead to INSERT/UPDATE/DELETE operations. The performance impact is usually negligible compared to the data integrity benefits. However, bulk operations can be optimized by temporarily disabling checks:
PRAGMA foreign_keys = OFF;
-- Bulk operations here
PRAGMA foreign_keys = ON;
PRAGMA foreign_key_check; -- Verify integrity after re-enablingSQLITE_CORRUPT_VTAB: Content in virtual table is corrupt
Content in virtual table is corrupt
SQLITE_IOERR_WRITE: Disk I/O error during write
Disk I/O error during write operation
SQLITE_READONLY: Attempt to write a readonly database
How to fix "SQLITE_READONLY: Attempt to write a readonly database" in SQLite
SQLITE_CONSTRAINT_PRIMARYKEY: PRIMARY KEY constraint failed
How to fix "SQLITE_CONSTRAINT_PRIMARYKEY" in SQLite
SQLITE_READONLY_DBMOVED: Database file has been moved since opened
How to fix 'SQLITE_READONLY_DBMOVED: Database file has been moved since opened'