SendGrid - NoMethodError (неопределенный метод `[]' для nil:NilClass)

Здравствуйте,

Мы начали использовать вебхук SendGrid с ключом проверки. Теперь получаем множество таких ошибок. Ранее мы отправили массовое приглашение через SendGrid.

app/controllers/webhooks_controller.rb:29:in `block in sendgrid'
app/controllers/webhooks_controller.rb:24:in `each'
app/controllers/webhooks_controller.rb:24:in `sendgrid'
actionpack (7.0.7) lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'
actionpack (7.0.7) lib/abstract_controller/base.rb:215:in `process_action'
actionpack (7.0.7) lib/action_controller/metal/rendering.rb:165:in `process_action'
actionpack (7.0.7) lib/abstract_controller/callbacks.rb:234:in `block in process_action'
activesupport (7.0.7) lib/active_support/callbacks.rb:107:in `run_callbacks'
actionpack (7.0.7) lib/abstract_controller/callbacks.rb:233:in `process_action'
actionpack (7.0.7) lib/action_controller/metal/rescue.rb:23:in `process_action'
actionpack (7.0.7) lib/action_controller/metal/instrumentation.rb:67:in `block in process_action'
activesupport (7.0.7) lib/active_support/notifications.rb:206:in `block in instrument'
activesupport (7.0.7) lib/active_support/notifications/instrumenter.rb:24:in `instrument'
activesupport (7.0.7) lib/active_support/notifications.rb:206:in `instrument'
actionpack (7.0.7) lib/action_controller/metal/instrumentation.rb:66:in `process_action'
actionpack (7.0.7) lib/action_controller/metal/params_wrapper.rb:259:in `process_action'
activerecord (7.0.7) lib/active_record/railties/controller_runtime.rb:27:in `process_action'
actionpack (7.0.7) lib/abstract_controller/base.rb:151:in `process'
actionview (7.0.7) lib/action_view/rendering.rb:39:in `process'
rack-mini-profiler (3.1.1) lib/mini_profiler/profiling_methods.rb:85:in `block in profile_method'
actionpack (7.0.7) lib/action_controller/metal.rb:188:in `dispatch'
actionpack (7.0.7) lib/action_controller/metal.rb:251:in `dispatch'
actionpack (7.0.7) lib/action_dispatch/routing/route_set.rb:49:in `dispatch'
actionpack (7.0.7) lib/action_dispatch/routing/route_set.rb:32:in `serve'
actionpack (7.0.7) lib/action_dispatch/journey/router.rb:50:in `block in serve'
actionpack (7.0.7) lib/action_dispatch/journey/router.rb:32:in `each'
actionpack (7.0.7) lib/action_dispatch/journey/router.rb:32:in `serve'
actionpack (7.0.7) lib/action_dispatch/routing/route_set.rb:852:in `call'
lib/middleware/omniauth_bypass_middleware.rb:64:in `call'
rack (2.2.8) lib/rack/tempfile_reaper.rb:15:in `call'
rack (2.2.8) lib/rack/conditional_get.rb:40:in `call'
rack (2.2.8) lib/rack/head.rb:12:in `call'
actionpack (7.0.7) lib/action_dispatch/http/permissions_policy.rb:38:in `call'
lib/content_security_policy/middleware.rb:12:in `call'
lib/middleware/anonymous_cache.rb:389:in `call'
lib/middleware/gtm_script_nonce_injector.rb:10:in `call'
rack (2.2.8) lib/rack/session/abstract/id.rb:266:in `context'
rack (2.2.8) lib/rack/session/abstract/id.rb:260:in `call'
actionpack (7.0.7) lib/action_dispatch/middleware/cookies.rb:704:in `call'
actionpack (7.0.7) lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
activesupport (7.0.7) lib/active_support/callbacks.rb:99:in `run_callbacks'
actionpack (7.0.7) lib/action_dispatch/middleware/callbacks.rb:26:in `call'
actionpack (7.0.7) lib/action_dispatch/middleware/debug_exceptions.rb:28:in `call'
actionpack (7.0.7) lib/action_dispatch/middleware/show_exceptions.rb:29:in `call'
logster (2.13.1) lib/logster/middleware/reporter.rb:40:in `call'
railties (7.0.7) lib/rails/rack/logger.rb:40:in `call_app'
railties (7.0.7) lib/rails/rack/logger.rb:27:in `call'
config/initializers/100-quiet_logger.rb:20:in `call'
config/initializers/100-silence_logger.rb:29:in `call'
actionpack (7.0.7) lib/action_dispatch/middleware/remote_ip.rb:93:in `call'
actionpack (7.0.7) lib/action_dispatch/middleware/request_id.rb:26:in `call'
lib/middleware/enforce_hostname.rb:24:in `call'
rack (2.2.8) lib/rack/method_override.rb:24:in `call'
actionpack (7.0.7) lib/action_dispatch/middleware/executor.rb:14:in `call'
rack (2.2.8) lib/rack/sendfile.rb:110:in `call'
actionpack (7.0.7) lib/action_dispatch/middleware/host_authorization.rb:131:in `call'
rack-mini-profiler (3.1.1) lib/mini_profiler.rb:260:in `call'
message_bus (4.3.8) lib/message_bus/rack/middleware.rb:60:in `call'
lib/middleware/request_tracker.rb:233:in `call'
railties (7.0.7) lib/rails/engine.rb:530:in `call'
railties (7.0.7) lib/rails/railtie.rb:226:in `public_send'
railties (7.0.7) lib/rails/railtie.rb:226:in `method_missing'
rack (2.2.8) lib/rack/urlmap.rb:74:in `block in call
rack (2.2.8) lib/rack/urlmap.rb:58:in `each'
rack (2.2.8) lib/rack/urlmap.rb:58:in `call'
unicorn (6.1.0) lib/unicorn/http_server.rb:634:in `process_client'
unicorn (6.1.0) lib/unicorn/http_server.rb:739:in `worker_loop'
unicorn (6.1.0) lib/unicorn/http_server.rb:547:in `spawn_missing_workers'
unicorn (6.1.0) lib/unicorn/http_server.rb:143:in `start'
unicorn (6.1.0) bin/unicorn:128:in `<top (required)>'
vendor/bundle/ruby/3.2.0/bin/unicorn:25:in `load'
vendor/bundle/ruby/3.2.0/bin/unicorn:25:in `<main>'

ENV

|REQUEST_URI|/webhooks/sendgrid|
|REQUEST_METHOD|POST|
|HTTP_USER_AGENT|SendGrid Event API|

Похоже, это та же проблема, что и здесь, которая была исправлена некоторое время назад: Sendgrid error: NoMethodError (undefined method `[]' for nil:NilClass) - #3 by Terrapop

Это сообщения о возвратах для адресов из вашего списка, которые не являются пользователями Discourse?

Не уверен, являются ли это отскоками. Как мы можем это проверить? Скорее всего, это произошло примерно в то время, когда мы рассылали массовые приглашения пользователям, не являющимся участниками Discourse.

Возможно, проблема связана с полезной нагрузкой события, получаемой от Sendgrid. Похоже, что поле status не устанавливается для событий bounce, как ожидалось.

Предполагая, что вы получили трассировку стека из logster по адресу <ВАШ_САЙТ>/logs, вкладка env должна содержать детали запроса. Не могли бы вы предоставить очищенную копию полезной нагрузки оттуда?

Там не так много информации, большую часть я уже указал здесь.

Для полного справочника, как и просили, я привёл его ниже

hostname serverhostname
process_id 248749
application_version a8d6dc4d3a5c3d937ff0bb162c93ec628428cda1
HTTP_HOST forum.URL.co.uk
REQUEST_URI /webhooks/sendgrid
REQUEST_METHOD POST
HTTP_USER_AGENT SendGrid Event API
HTTP_X_FORWARDED_FOR IPADDRESS
HTTP_X_REAL_IP IPADDRESS
time Вс 17:54

Извините, я должен был быть более конкретным. В теле запроса содержатся необходимые подсказки. Вы должны найти их после строки time. Возможно, вам потребуется прокрутить вверх или развернуть панель, чтобы увидеть это.

Здравствуйте,

После строки «Время» нет никакой дополнительной информации. Я проверил все 34 ошибки. Это кажется странным, почему она не включена?

Да, это странно. Не могли бы вы получить полезную нагрузку из панели управления SendGrid?

Согласно последним документам по веб-хукам SendGrid, текущая конечная точка в Discourse должна работать без проблем, но ошибка указывает на проблему с полезной нагрузкой запроса, поэтому пример был бы очень полезен.

Вы когда-нибудь разобрались с этим? У меня та же ошибка, на той же строке кода:

Я не знаком с Ruby, но кажется, что он успешно парсит JSON до момента попытки разобрать код ошибки. Email::SMTP_STATUS_TRANSIENT_FAILURE указывает на:

Я проверил в Webhook.site, что именно отправляет Sendgrid при тестировании вебхука, и для отскока (bounce) это выглядит так:

[
  {
    "email": "example@test.com",
    "timestamp": 1740136261,
    "smtp-id": "<14c5d75ce93.dfd.64b469@ismtpd-555>",
    "event": "bounce",
    "category": [
      "cat facts"
    ],
    "sg_event_id": "ovGQ2rRo8ytNezHPDq-7Ig==",
    "sg_message_id": "14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0",
    "reason": "500 unknown recipient",
    "status": "5.0.0"
  }
]

Кажется, всё должно работать!

Ага, отвечаю сам на свой вопрос: кнопка «Тест интеграции» в вебхуке вас обманывает.

Вот реальный payload, если отправить письмо на несуществующий адрес:

[
  {
    "bounce_classification": "Unclassified",
    "email": "noemail@this.does.not.exist.tld",
    "event": "bounce",
    "reason": "unable to get mx info: failed to get IPs from PTR record: lookup <nil>: unrecognized address",
    "sg_event_id": "Ym91bmNlLTQtNTA0ODUxOTUtZXVvMmlLeGRTYXlQRjRZRTQtLUk3QS0w",
    "sg_message_id": "euo2iKxdSayPF4YE4--I7A.recvd-5f54b5d587-pczjm-1-67BADEEA-6.0",
    "smtp-id": "<870b3a2a-160c-4fc8-bc9a-bd0d5b943b81@forum.umbraco.com>",
    "timestamp": 1740300320,
    "tls": 0,
    "type": "blocked"
  }
]

И вот оно: поля status нет.

Это происходит, когда я намеренно использую несуществующий домен. Мне кажется, это должно обрабатываться, возможно, "type": "blocked" может служить индикатором, который ищет Discourse.

В другой попытке я добавил что-то бессмысленное перед outlook.com, и это дало мне рабочий payload:

[
  {
    "bounce_classification": "Invalid Address",
    "email": "oeihoiwehitwiohtriuweruiwerwierhwuerguiwerg@outlook.com",
    "event": "bounce",
    "ip": "149.72.1.78",
    "reason": "550 5.5.0 Requested action not taken: mailbox unavailable (S2017062302). [HK3PEPF0000021E.apcprd03.prod.outlook.com 2025-02-23T08:54:35.950Z 08DD502499E1A0AA]",
    "sg_event_id": "Ym91bmNlLTAtNTA0ODUxOTUtQWJFZ2pVejZUUFd3MnJNTnJabDg4Zy0w",
    "sg_message_id": "AbEgjUz6TPWw2rMNrZl88g.recvd-786d47b7ff-tsp86-1-67BAE249-D.0",
    "smtp-id": "<d8dc253e-6e9a-4418-8478-60780eb4898c@forum.umbraco.com>",
    "status": "5.5.0",
    "timestamp": 1740300876,
    "tls": 1,
    "type": "bounce"
  }
]

Создал PR!

@cultiv Спасибо, что углубились в эту проблему и нашли время отправить PR. Я использовал ваш PR в качестве основы и объединил исправление проблемы по ссылке FIX: No method error in `WebhooksController#sendgrid` (#31495) · discourse/discourse@209d289 · GitHub.

2 лайка

Отлично, это было очень полезно. Сам бы я со всем этим не справился :sweat_smile:

Спасибо, я обновил, и теперь ручное тестирование проходит правильно: журнал ошибок чист, а в логe почты видно, что письмо отклонено :raising_hands:

1 лайк