Error de 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 necesita un árbitro: una restricción unique (o de exclusión) que defina qué significa "conflicto". El conflict target que escribiste debe coincidir con una exactamente — mismo conjunto de columnas, misma expresión, mismo predicado parcial. Sin coincidencia, no hay upsert.

Qué significa este error

PostgreSQL no adivina a qué restricción te refieres. ON CONFLICT (email) requiere un índice único sobre exactamente (email):

Causas comunes

Cómo diagnosticarlo

\d users   -- lista índices y restricciones con su definición exacta

Compara el conflict target del INSERT que falla con las definiciones de índice, comprobando columnas, expresiones y cláusulas WHERE literalmente.

Cómo solucionarlo

-- Falta la restricción → créala (en producción: CONCURRENTLY)
CREATE UNIQUE INDEX CONCURRENTLY users_email_key ON users (email);

-- Índice de expresión → el target debe repetirla:
INSERT INTO users (email) VALUES ('[email protected]')
ON CONFLICT (lower(email)) DO NOTHING;

-- Índice parcial → el target debe repetir el predicado:
INSERT INTO users (email) VALUES ('[email protected]')
ON CONFLICT (email) WHERE deleted_at IS NULL DO NOTHING;

Alternativamente, nombra la restricción directamente — ON CONFLICT ON CONSTRAINT users_email_key — aunque la forma de columna sobrevive mejor a los renombrados de restricción.

🔍 El error que ON CONFLICT existe para prevenir: duplicate key value violates unique constraint.
🔍 Lectura relacionada: Elegir el índice adecuado — índices únicos, parciales y de expresión.