Erreur PostgreSQL : no unique or exclusion constraint matching the ON CONFLICT specification

ERROR:  there is no unique or exclusion constraint matching the ON CONFLICT specification

INSERT ... ON CONFLICT a besoin d'un arbitre : une contrainte d'unicité (ou d'exclusion) qui définit ce que « conflit » signifie. La cible du conflit que vous avez écrite doit en correspondre une exactement — même ensemble de colonnes, même expression, même prédicat partiel. Aucune correspondance, aucun upsert.

Ce que signifie cette erreur

PostgreSQL ne devine pas quelle contrainte vous voulez dire. ON CONFLICT (email) exige un index unique sur exactement (email) :

Causes fréquentes

Comment la diagnostiquer

\d users   -- liste les index et contraintes avec leur définition exacte

Comparez la cible du conflit dans l'INSERT en échec aux définitions des index, en vérifiant colonnes, expressions et clauses WHERE littéralement.

Comment la corriger

-- La contrainte manque → créez-la (en production : CONCURRENTLY)
CREATE UNIQUE INDEX CONCURRENTLY users_email_key ON users (email);

-- Index sur expression → la cible doit la répéter :
INSERT INTO users (email) VALUES ('[email protected]')
ON CONFLICT (lower(email)) DO NOTHING;

-- Index partiel → la cible doit répéter le prédicat :
INSERT INTO users (email) VALUES ('[email protected]')
ON CONFLICT (email) WHERE deleted_at IS NULL DO NOTHING;

Alternativement, nommez directement la contrainte — ON CONFLICT ON CONSTRAINT users_email_key — bien que la forme par colonnes survive mieux aux renommages de contraintes.

🔍 L'erreur que ON CONFLICT existe pour prévenir : duplicate key value violates unique constraint.
🔍 À lire aussi : Choisir le bon index — les index uniques, partiels et sur expression.