Skip to content
SQLite

SQLite: UNIQUE constraint failed

UNIQUE constraint failed (duplicate value in a UNIQUE or PRIMARY KEY column)

Applies to: SQLite 3.x (examples run on 3.39.2; shell output from 3.50.6)
Last technically reviewed 24 October 2026 on SQLite 3.39.2 (PHP PDO_SQLITE) and the sqlite3 shell 3.50.6
The full message
UNIQUE constraint failed: customers.email
Other forms of this message
A PRIMARY KEY (same text, extended code 1555)
UNIQUE constraint failed: customers.id
Multi-column key (every key column is listed)
UNIQUE constraint failed: order_lines.order_id, order_lines.line_no
sqlite3 shell (3.50.6), running a script
Runtime error near line 3: UNIQUE constraint failed: customers.email (19)
PHP PDO
SQLSTATE[23000]: Integrity constraint violation: 19 UNIQUE constraint failed: customers.email
Python sqlite3 (raised as sqlite3.IntegrityError)
sqlite3.IntegrityError: UNIQUE constraint failed: customers.email
Result code
SQLITE_CONSTRAINT (19); extended SQLITE_CONSTRAINT_UNIQUE (2067) or SQLITE_CONSTRAINT_PRIMARYKEY (1555)
Product
SQLite library
Versions
SQLite 3.x (examples run on 3.39.2; shell output from 3.50.6)
Last reviewed
24 October 2026

What it means

This error means a row would store a value that already exists in a column (or set of columns) that must be unique, so SQLite rejected the statement.

A UNIQUE constraint, a unique index or a PRIMARY KEY allows each value only once. When an INSERT or UPDATE would create a second row with the same value, SQLite stops the statement and reports the table and column that rejected it (customers.email). For a key over several columns, every column of the key is listed.

SQLite uses the same message for PRIMARY KEY conflicts; only the extended result code differs (1555 instead of 2067), and most drivers show just the primary code, 19 (SQLITE_CONSTRAINT). A column declared INTEGER PRIMARY KEY is an alias for the table's rowid: leave it out of the INSERT and SQLite assigns the next free value, which avoids most duplicate-id errors. Other constraint failures share code 19 too, such as NOT NULL constraint failed and FOREIGN KEY constraint failed, so check the message, not only the code.

Common causes

  • The same row is inserted twice: a retried request, a double-tapped button or an import or migration script that runs again.
  • The application supplies ids itself (for example copied from a server database or another device) and one of them is already taken.
  • An UPDATE changes a unique column, such as an email address, to a value another row already has.
  • CREATE UNIQUE INDEX is run on a column that already contains duplicate values.

Diagnose it

Read-only These statements only read the schema (sqlite_schema and read-only PRAGMA queries) or your own data. They do not change data, settings or the database file, so they are safe to run first.

Which unique indexes does the table have?

UNIQUE and PRIMARY KEY constraints are enforced by indexes; constraints written in CREATE TABLE get automatic names such as sqlite_autoindex_customers_1. A rowid alias (INTEGER PRIMARY KEY) has no separate index.

PRAGMA index_list(customers);
PRAGMA index_info(sqlite_autoindex_customers_1);
What to look for

index_list shows each index with unique = 1 for unique ones and origin u (UNIQUE), pk (PRIMARY KEY) or c (CREATE INDEX). index_info lists the columns of one index.

Find the row that already holds the value

SELECT *
FROM customers
WHERE email = 'sara@example.com';  -- the value you tried to write
What to look for

The existing row. Compare it with the data you were writing to decide whether to skip the new row, update the existing one or reject the request.

Find duplicates before adding a unique index

SELECT name, COUNT(*) AS copies
FROM customers
GROUP BY name
HAVING COUNT(*) > 1
ORDER BY copies DESC;
What to look for

Each value that appears more than once. CREATE UNIQUE INDEX on that column fails until these are resolved.

Read the table definition

SELECT sql
FROM sqlite_schema
WHERE type IN ('table', 'index') AND tbl_name = 'customers';
What to look for

The CREATE statements for the table and its indexes, including UNIQUE and PRIMARY KEY clauses. Older versions name this table sqlite_master, and every version still accepts that name.

How to fix it

Each fix is labelled with what it changes. Try read-only checks and the least invasive fix first, and test changes outside production.

Update the existing row instead (upsert)

Changes data

When a matching value should update the row rather than fail, use INSERT ... ON CONFLICT (column) DO UPDATE (available from SQLite 3.24.0). excluded refers to the row you tried to insert. Name the conflict column so only that constraint is handled.

INSERT INTO customers (email, name)
VALUES ('sara@example.com', 'Sara Ahmed')
ON CONFLICT (email) DO UPDATE SET name = excluded.name;

Skip rows whose value already exists

Changes data

For re-runs and imports, insert only what is missing. ON CONFLICT DO NOTHING skips just the conflicting row and keeps the rest of a multi-row insert.

Caution: INSERT OR REPLACE also avoids the error, but it deletes the existing row and inserts a new one: columns you did not supply are lost, the rowid can change, and ON DELETE actions on child tables run. Use an upsert instead unless you really want the row replaced.

INSERT INTO customers (email, name)
VALUES ('sara@example.com', 'Sara'), ('ali@example.com', 'Ali')
ON CONFLICT (email) DO NOTHING;

Let SQLite assign the key

Changes data

With an INTEGER PRIMARY KEY column, leave the id out (or pass NULL) and SQLite assigns one. Read it back with last_insert_rowid() or your driver's equivalent, or with RETURNING on SQLite 3.35.0 and later.

INSERT INTO customers (email, name) VALUES ('ali@example.com', 'Ali');  -- no id supplied
SELECT last_insert_rowid();

Resolve existing duplicates, then add the unique index

Changes schema

Find the duplicates with the read-only query above and agree which row to keep (or merge them). Then create the index. Take a copy of the database file first.

CREATE UNIQUE INDEX ux_customers_name ON customers (name);

Examples

Inserting an email address that is already registered

CREATE TABLE customers (
    id    INTEGER PRIMARY KEY,
    email TEXT UNIQUE,
    name  TEXT
);

INSERT INTO customers (email, name) VALUES ('sara@example.com', 'Sara');
INSERT INTO customers (email, name) VALUES ('sara@example.com', 'Sara B');
Result
UNIQUE constraint failed: customers.email

Reproduced on SQLite 3.39.2, 24 October 2026.

Supplying a primary key value that is taken

CREATE TABLE customers (
    id    INTEGER PRIMARY KEY,
    email TEXT,
    name  TEXT
);

INSERT INTO customers (id, email, name) VALUES (1, 'sara@example.com', 'Sara');
INSERT INTO customers (id, email, name) VALUES (1, 'ali@example.com', 'Ali');  -- id 1 already exists
Result
UNIQUE constraint failed: customers.id

Reproduced on SQLite 3.39.2, 24 October 2026.

A duplicate in a multi-column key

CREATE TABLE order_lines (
    order_id INTEGER,
    line_no  INTEGER,
    PRIMARY KEY (order_id, line_no)
);

INSERT INTO order_lines (order_id, line_no) VALUES (10, 1);
INSERT INTO order_lines (order_id, line_no) VALUES (10, 1);
Result
UNIQUE constraint failed: order_lines.order_id, order_lines.line_no

Reproduced on SQLite 3.39.2, 24 October 2026.

Adding a unique index over existing duplicates

CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT);
INSERT INTO customers (name) VALUES ('Sara'), ('Sara');

CREATE UNIQUE INDEX ux_customers_name ON customers (name);
Result
UNIQUE constraint failed: customers.name

Reproduced on SQLite 3.39.2, 24 October 2026.

How to prevent it

Let SQLite assign surrogate keys with INTEGER PRIMARY KEY, and make writes that may run twice idempotent: an upsert for "create or update" actions, ON CONFLICT DO NOTHING for imports. When data comes from several devices or servers, use a natural unique key (such as an email address or a UUID) rather than copying numeric ids between databases.

Keep the unique constraint even when the application checks first, and handle the constraint error in your code (in Python, catch sqlite3.IntegrityError and check the message) so a duplicate becomes a clear message such as "this email is already registered".

Common questions

How do I fix "UNIQUE constraint failed" in SQLite?

Read the table and column from the message, find the row that already holds the value, then skip the new row (ON CONFLICT DO NOTHING), update the existing one (ON CONFLICT DO UPDATE), or stop supplying ids and let INTEGER PRIMARY KEY assign them.

Why does the message say UNIQUE when it is my PRIMARY KEY?

SQLite reports PRIMARY KEY conflicts with the same "UNIQUE constraint failed" text. Only the extended result code tells them apart: 1555 (SQLITE_CONSTRAINT_PRIMARYKEY) for a primary key, 2067 (SQLITE_CONSTRAINT_UNIQUE) for a UNIQUE constraint or index.

Is INSERT OR REPLACE a safe way to avoid the error?

Usually not. REPLACE deletes the conflicting row and inserts a new one, so columns you did not supply become their defaults and delete actions on related tables run. An upsert with ON CONFLICT DO UPDATE changes only the columns you name.

Learn more on CodeWithSQL

Official documentation

Last technically reviewed 24 October 2026; examples and diagnostics run on SQLite 3.39.2 (PHP PDO_SQLITE) and the sqlite3 shell 3.50.6. Messages are the text the SQLite library itself returns (sqlite3_errmsg) for the versions shown.

Still stuck on a SQLite error?

Browse the full SQLite error library, or get hands-on help from an experienced SQL professional.