Ffile2fix
Sign in Get started

Fix "Duplicate entry for key" in MySQL (error 1062)

ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'

An INSERT or UPDATE would put a value into a PRIMARY KEY or UNIQUE index that already holds that value. Find the existing row with that value, then either change the data, update the existing row instead (upsert), or repair a broken AUTO_INCREMENT.

Also appears as: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '[email protected]' for key 'users.users_email_unique' · ERROR 1062 (23000): Duplicate entry '42' for key 'wp_posts.PRIMARY' · WordPress database error Duplicate entry '0' for key 'PRIMARY' · Duplicate entry '' for key 'username'

Common causes

  • Importing a dump into a table that already contains the same rows
  • The id column lost its AUTO_INCREMENT attribute after a migration, so every insert uses 0 (common in WordPress: Duplicate entry '0')
  • The AUTO_INCREMENT counter is behind the highest existing id after a partial import or manual insert
  • Users submitting the same unique value twice, such as an email or username, or a double form submit
  • Empty strings counting as a value in a UNIQUE column, while NULLs do not
  • Two concurrent requests both checking 'does it exist?' and then both inserting

How to fix it

  1. Find the existing row. Read the value and key name from the message and query it: SELECT * FROM users WHERE email = '[email protected]'; MySQL 8.0.19+ prefixes the key with the table name, such as users.PRIMARY.
  2. Check AUTO_INCREMENT on the id. Run SHOW CREATE TABLE wp_posts; and confirm the id column says AUTO_INCREMENT. If it is missing, restore it with ALTER TABLE ... MODIFY id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT; after removing any row with id 0.
  3. Move the counter past the highest id. Check SELECT MAX(id) FROM t; and set ALTER TABLE t AUTO_INCREMENT = <max+1>; so new rows get unused ids.
  4. Use an upsert when the row may exist. INSERT ... ON DUPLICATE KEY UPDATE updates the existing row instead of failing. This also removes the race between a SELECT check and an INSERT.
  5. Import into an empty table or skip duplicates. Drop or truncate the target table before importing, or change INSERT INTO to INSERT IGNORE INTO in the dump. INSERT IGNORE also hides other errors, so review the warnings afterwards.
  6. Validate input in the app. Catch SQLSTATE 23000 and show a friendly 'already registered' message, and disable the submit button after the first click.

SQL

-- update instead of failing when the key exists
INSERT INTO users (email, name) VALUES ('[email protected]', 'Ana')
  ON DUPLICATE KEY UPDATE name = VALUES(name);

-- realign the counter
SELECT MAX(id) FROM users;
ALTER TABLE users AUTO_INCREMENT = 1001;

How to stop it happening again

  • Keep AUTO_INCREMENT on primary keys and check it after migrations or engine conversions
  • Use upserts or unique-index error handling instead of select-then-insert checks
  • Import dumps into empty databases or use tools that drop tables first (mysqldump --add-drop-table)
  • Store NULL rather than empty strings in optional unique columns

Frequently asked questions

Why does WordPress say Duplicate entry '0' for key 'PRIMARY'?

The ID columns lost AUTO_INCREMENT, usually after a bad migration or export, so every new row tries id 0. Restore AUTO_INCREMENT on the affected tables' primary keys and remove the row stored with id 0.

Is INSERT IGNORE safe?

It skips duplicate rows, but it also turns some other errors, such as invalid values, into warnings. Use it for known duplicate imports and check SHOW WARNINGS afterwards.

Can a UNIQUE column hold several NULLs?

Yes. In InnoDB a UNIQUE index allows multiple NULL values but only one empty string, so convert empty strings to NULL if the field is optional.