From 6b7180d051a1338d66cd8f5f9939b6cb1076c0eb Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Fri, 13 Feb 2026 14:06:12 -0800 Subject: [PATCH] fix(twilio): prevent dead jobs on missing channel lookup (#13522) ## Why We observed `Webhooks::TwilioEventsJob` failures ending up in Sidekiq dead jobs when Twilio callback payloads could not be mapped to a `Channel::TwilioSms` record. In this scenario, channel lookup raised `ActiveRecord::RecordNotFound`, which caused retries and eventual dead jobs instead of a graceful drop. Related Sentry issue/search: - https://chatwoot-p3.sentry.io/issues/?project=6382945&query=Webhooks%3A%3ATwilioEventsJob%20ActiveRecord%3A%3ARecordNotFound ## What changed This PR keeps the existing lookup flow but makes it non-raising: - `app/services/twilio/incoming_message_service.rb` - `find_by!` -> `find_by` for account SID + phone lookup - Added warning log when channel lookup misses - `app/services/twilio/delivery_status_service.rb` - `find_by!` -> `find_by` for account SID + phone lookup - Added warning log when channel lookup misses ## Reproduction Configure a Twilio webhook callback that reaches Chatwoot but does not match an existing Twilio channel lookup path. Before this change, the job raises `RecordNotFound` and can end up in dead jobs after retries. After this change, the job logs the miss and exits safely. ## Testing - `bundle exec rspec spec/services/twilio/incoming_message_service_spec.rb spec/services/twilio/delivery_status_service_spec.rb` - `bundle exec rubocop app/services/twilio/incoming_message_service.rb app/services/twilio/delivery_status_service.rb` --- app/services/twilio/delivery_status_service.rb | 14 +++++++++++++- app/services/twilio/incoming_message_service.rb | 15 +++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/app/services/twilio/delivery_status_service.rb b/app/services/twilio/delivery_status_service.rb index bf8422fcd..bed390aa5 100644 --- a/app/services/twilio/delivery_status_service.rb +++ b/app/services/twilio/delivery_status_service.rb @@ -47,8 +47,10 @@ class Twilio::DeliveryStatusService @twilio_channel ||= if params[:MessagingServiceSid].present? ::Channel::TwilioSms.find_by(messaging_service_sid: params[:MessagingServiceSid]) elsif params[:AccountSid].present? && params[:From].present? - ::Channel::TwilioSms.find_by!(account_sid: params[:AccountSid], phone_number: params[:From]) + ::Channel::TwilioSms.find_by(account_sid: params[:AccountSid], phone_number: params[:From]) end + log_channel_not_found if @twilio_channel.blank? + @twilio_channel end def message @@ -56,4 +58,14 @@ class Twilio::DeliveryStatusService @message ||= twilio_channel.inbox.messages.find_by(source_id: params[:MessageSid]) end + + def log_channel_not_found + Rails.logger.warn( + '[TWILIO] Delivery status channel lookup failed ' \ + "account_sid=#{params[:AccountSid]} " \ + "from=#{params[:From]} " \ + "messaging_service_sid=#{params[:MessagingServiceSid]} " \ + "message_sid=#{params[:MessageSid]}" + ) + end end diff --git a/app/services/twilio/incoming_message_service.rb b/app/services/twilio/incoming_message_service.rb index 5d695ebb2..d67b6d515 100644 --- a/app/services/twilio/incoming_message_service.rb +++ b/app/services/twilio/incoming_message_service.rb @@ -26,12 +26,23 @@ class Twilio::IncomingMessageService def twilio_channel @twilio_channel ||= ::Channel::TwilioSms.find_by(messaging_service_sid: params[:MessagingServiceSid]) if params[:MessagingServiceSid].present? if params[:AccountSid].present? && params[:To].present? - @twilio_channel ||= ::Channel::TwilioSms.find_by!(account_sid: params[:AccountSid], - phone_number: params[:To]) + @twilio_channel ||= ::Channel::TwilioSms.find_by(account_sid: params[:AccountSid], + phone_number: params[:To]) end + log_channel_not_found if @twilio_channel.blank? @twilio_channel end + def log_channel_not_found + Rails.logger.warn( + '[TWILIO] Incoming message channel lookup failed ' \ + "account_sid=#{params[:AccountSid]} " \ + "to=#{params[:To]} " \ + "messaging_service_sid=#{params[:MessagingServiceSid]} " \ + "sms_sid=#{params[:SmsSid]}" + ) + end + def inbox @inbox ||= twilio_channel.inbox end