Validación fallida: el correo electrónico principal ya está en uso al intentar manejar variantes con puntos de Gmail o agregar correos secundarios

Versión de Discourse: 2026.5.0-latest.1

Contexto

Cuando un usuario externo envía un correo electrónico al gestor de correo entrante utilizando una variante con «punto» de Gmail (por ejemplo, user.name@gmail.com), pero su cuenta registrada en el foro es la versión principal sin puntos (username@gmail.com), el gestor de correo entrante falla con una excepción no manejada: ActiveRecord::RecordInvalid (Validation failed: Primary email has already been taken).

Además, intentar resolver esto añadiendo la variante con puntos como correo secundario al perfil del usuario —ya sea mediante la interfaz de usuario o la capa de modelos de Rails Console usando UserEmail.create!— falla con el mismo error de bucle de validación. La única solución alternativa es una inyección SQL directa en la base de datos, eludiendo ActiveRecord.

Pasos para reproducir

  1. Crea una cuenta de usuario en Discourse con el correo principal username@gmail.com.

  2. Haz que ese usuario envíe un correo entrante a una dirección de categoría/respuesta desde user.name@gmail.com.

  3. Observa el rechazo en los registros de correo entrante debido a ActiveRecord::RecordInvalid.

  4. Intenta añadir user.name@gmail.com como correo secundario a la cuenta username@gmail.com desde la consola de Rails:

    UserEmail.create!(user_id: target_id, email: 'user.name@gmail.com', primary: false)
    
    
  5. Observa el fallo de validación del modelo.

Comportamiento esperado

Discourse debe manejar la normalización de Gmail de forma correcta. Debería:

  1. Reconocer la variante con puntos de Gmail entrante como perteneciente a la cuenta principal de forma transparente durante la fase de gestión del correo entrante.

  2. Como mínimo, permitir que un administrador añada la variante con puntos como correo secundario a la cuenta principal sin desencadenar un bloqueo de la aplicación por «correo principal ya tomado», ya que pertenece al mismo usuario y está explícitamente configurado como primary: false.

Comportamiento real

La capa de aplicación queda atrapada en un bucle lógico:

  • Detecta user.name@gmail.com como una cadena «nueva», por lo que intenta actuar sobre ella (crear un usuario provisional o añadir un correo secundario).

  • Durante la fase de validación, el modelo UserEmail ejecuta su lógica de normalización de Gmail, elimina los puntos, detecta que username@gmail.com ya es el índice de correo principal para ese user_id y bloquea su propia ejecución bajo la falsa premisa de que se está produciendo un conflicto de registro duplicado.

Solución alternativa utilizada para desbloquear

La única forma de resolver esto fue conectarse por SSH al contenedor y ejecutar SQL directo para eludir completamente las validaciones de ActiveRecord:

sql = "INSERT INTO user_emails (user_id, email, \"primary\", created_at, updated_at) VALUES (X, 'user.name@gmail.com', false, NOW(), NOW())"
ActiveRecord::Base.connection.execute(sql)

Una vez forzado mediante SQL directo, el seguimiento del correo entrante funciona perfectamente. El código de validación debería actualizarse para tener en cuenta este caso extremo.

¡Gracias!

2 Me gusta