Errore 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 ha bisogno di un arbitro: un vincolo unique (o di esclusione) che definisce cosa significa "conflitto". Il conflict target che scrivi deve corrisponderne uno esattamente — stesso insieme di colonne, stessa espressione, stesso predicato parziale. Nessuna corrispondenza, nessun upsert.

Cosa significa questo errore

PostgreSQL non indovina quale vincolo intendi. ON CONFLICT (email) richiede un indice unique esattamente su (email):

Cause comuni

Come diagnosticarlo

\d users   -- elenca indici e vincoli con la loro definizione esatta

Confronta il conflict target nell'INSERT fallito con le definizioni degli indici, controllando colonne, espressioni e clausole WHERE alla lettera.

Come risolverlo

-- Manca il vincolo → crealo (in produzione: CONCURRENTLY)
CREATE UNIQUE INDEX CONCURRENTLY users_email_key ON users (email);

-- Indice su espressione → il target deve ripeterla:
INSERT INTO users (email) VALUES ('[email protected]')
ON CONFLICT (lower(email)) DO NOTHING;

-- Indice parziale → il target deve ripetere il predicato:
INSERT INTO users (email) VALUES ('[email protected]')
ON CONFLICT (email) WHERE deleted_at IS NULL DO NOTHING;

In alternativa, nomina il vincolo direttamente — ON CONFLICT ON CONSTRAINT users_email_key — anche se la forma con le colonne sopravvive meglio ai rename dei vincoli.

🔍 L'errore che ON CONFLICT esiste per prevenire: duplicate key value violates unique constraint.
🔍 Lettura correlata: Scegliere l'indice giusto — indici unique, parziali e su espressione.