Validação falhou: o e-mail principal já está em uso ao tentar lidar com variantes de ponto do Gmail ou adicionar e-mails secundários

Versão do Discourse: 2026.5.0-latest.1

Contexto

Quando um usuário externo envia um e-mail ao gerenciador de e-mails recebidos usando uma variante com “ponto” do Gmail (por exemplo, user.name@gmail.com), mas sua conta registrada no fórum é a versão primária sem pontos (username@gmail.com), o gerenciador de e-mails recebidos falha com uma exceção não tratada: ActiveRecord::RecordInvalid (Validation failed: Primary email has already been taken).

Além disso, tentar resolver isso adicionando a variante com ponto como um e-mail secundário ao perfil do usuário — seja pela interface do usuário ou pela camada de modelo do Rails Console usando UserEmail.create! — falha com o mesmo erro de loop de validação. A única solução alternativa é uma injeção SQL direta no banco de dados, contornando o ActiveRecord.

Passos para Reproduzir

  1. Crie uma conta de usuário no Discourse com o e-mail primário username@gmail.com.

  2. Peça que esse usuário envie um e-mail recebido para um endereço de categoria/resposta a partir de user.name@gmail.com.

  3. Observe a rejeição nos logs de e-mails recebidos devido ao erro ActiveRecord::RecordInvalid.

  4. Tente adicionar user.name@gmail.com como um e-mail secundário à conta username@gmail.com via console do Rails:

    UserEmail.create!(user_id: target_id, email: 'user.name@gmail.com', primary: false)
    
    
  5. Observe a falha na validação do modelo.

Comportamento Esperado

O Discourse deve lidar com a normalização do Gmail de forma adequada. Ele deveria:

  1. Reconhecer a variante do Gmail com ponto recebida como pertencente à conta primária de forma transparente durante o processamento de e-mails recebidos.

  2. Ou, pelo menos, permitir que um administrador adicione a variante com ponto como um e-mail secundário à conta principal sem disparar um bloqueio de aplicação de “E-mail primário já utilizado”, já que pertence ao mesmo usuário e está explicitamente definido como primary: false.

Comportamento Atual

A camada de aplicação fica presa em um loop lógico:

  • Ela vê user.name@gmail.com como uma string “nova”, então tenta agir sobre ela (criar um usuário em estágio ou adicionar um e-mail secundário).

  • Durante a fase de validação, o modelo UserEmail executa sua lógica de normalização do Gmail, remove os pontos, verifica que username@gmail.com já é o índice de e-mail primário para aquele user_id e bloqueia sua própria execução sob a falsa premissa de que está ocorrendo um conflito de registro duplicado.

Solução Alternativa Utilizada para Desbloquear

A única maneira de resolver isso foi fazer SSH no contêiner e executar SQL direto para contornar completamente as validações do 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)

Uma vez forçado via SQL direto, o rastreamento de e-mails recebidos funciona perfeitamente. O código de validação deve ser atualizado para levar em conta esse caso limite.

Obrigado!

2 curtidas