Ошибка валидации: основной адрес электронной почты уже занят при попытке обработки вариантов Gmail с точками или добавления вторичных адресов

Версия Discourse: 2026.5.0-latest.1

Контекст

Когда внешний пользователь отправляет электронное письмо обработчику входящей почты, используя вариант с точкой в адресе Gmail (например, user.name@gmail.com), но его зарегистрированный аккаунт на форуме привязан к основному адресу без точек (username@gmail.com), обработчик входящей почты падает с необработанной ошибкой: ActiveRecord::RecordInvalid (Validation failed: Primary email has already been taken).

Более того, попытка решить эту проблему путём добавления варианта с точкой как дополнительного адреса электронной почты в профиль пользователя — будь то через интерфейс или на уровне модели Rails Console с использованием UserEmail.create! — завершается неудачей с той же ошибкой цикла валидации. Единственным обходным путём является прямое выполнение SQL-запроса к базе данных, минуя ActiveRecord.

Шаги для воспроизведения

  1. Создайте аккаунт пользователя в Discourse с основным адресом username@gmail.com.

  2. Попросите этого пользователя отправить входящее письмо на адрес категории/ответа с адреса user.name@gmail.com.

  3. Обратите внимание на отказ в логах входящей почты из-за ошибки ActiveRecord::RecordInvalid.

  4. Попробуйте добавить user.name@gmail.com как дополнительный адрес к аккаунту username@gmail.com через консоль Rails:

    UserEmail.create!(user_id: target_id, email: 'user.name@gmail.com', primary: false)
    
    
  5. Обратите внимание на сбой валидации модели.

Ожидаемое поведение

Discourse должен корректно обрабатывать нормализацию адресов Gmail. Он должен либо:

  1. Без проблем распознавать входящий вариант Gmail с точками как принадлежащий основному аккаунту на этапе обработки входящей почты.

  2. Как минимум, позволять администратору добавлять вариант с точкой в качестве дополнительного адреса к основному аккаунту, не вызывая блокировки приложения с сообщением «Основной адрес занят», поскольку он принадлежит тому же пользователю и явно установлен как primary: false.

Фактическое поведение

Уровень приложения попадает в логический цикл:

  • Он видит user.name@gmail.com как «новый» строковый идентификатор и пытается выполнить действия с ним (создать временного пользователя или добавить дополнительный адрес).

  • На этапе валидации модель UserEmail запускает свою логику нормализации Gmail, удаляет точки, обнаруживает, что username@gmail.com уже является основным индексом адреса для данного user_id, и блокирует собственное выполнение, ошибочно полагая, что происходит конфликт дублирующихся записей.

Использованный обходной путь для разблокировки

Единственный способ решить проблему — подключиться по SSH к контейнеру и выполнить прямой SQL-запрос, полностью минуя валидации 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)

После принудительного добавления через прямой SQL отслеживание входящей почты работает безупречно. Код валидации следует обновить, чтобы учесть этот частный случай.

Спасибо!

2 лайка