user762579
user762579

Reputation:

Rails 4 ActionMailer - How to capture connection and SMTP errors when using deliver_later

I am using ActionMailer deliver_later w ActiveJobs / Sidekiq using :

config.active_job.queue_adapter = :sidekiq

as I am testing in dev mode w MailCatcher, the only error I can catch is Errno::ECONNREFUSED, setting port 1026 instead of 1025

When I test deliver_now, I get the Errno::ECONNREFUSED error raised, which is fine

begin
   MessageMailer.contact_me_email(@message).deliver_now
   rescue Errno::ECONNREFUSED, Net::SMTPAuthenticationError, Net::SMTPServerBusy, Net::SMTPSyntaxError, Net::SMTPFatalError, Net::SMTPUnknownError => e
     flash[:error] = "Problems sending mail. Please try again later"
     # delete message or resend it ?
     byebug
     @message.destroy
     format.html { render :new }
   end

However, when I request a deliver_later, then the request is sore in the sidekiq mailers queue, and the error is not captured...

       begin
   MessageMailer.contact_me_email(@message).deliver_later(wait: 1.minute ) # in mailers queue
   rescue Errno::ECONNREFUSED, Net::SMTPAuthenticationError, Net::SMTPServerBusy, Net::SMTPSyntaxError, Net::SMTPFatalError, Net::SMTPUnknownError => e
     flash[:error] = "Problems sending mail. Please try again later"
     # delete message or resend it ?
     byebug
     @message.destroy
     format.html { render :new }
   end

How can I capture and rescue from SMTP errors in this case ? I guess it's a Sidekiq responsibility to handle it ... any hint welcome..

Upvotes: 6

Views: 3088

Answers (1)

brendan
brendan

Reputation: 12111

ActiveJobs allows you to rescue errors that occur when the job is performed using the rescue_from method. See the api. You will need to implement this method in your ActiveJob subclasses.

Since ActionMailer uses DeliveryJobs to deliver the messages as jobs you will need to add a rescue_from method to this class to handle the exception.

For example, you could place in config/initializers/action_mailer.rb:

ActionMailer::DeliveryJob.rescue_from(StandardError) do |exception|
    Rails.logger.error "Original record not found: #{@serialized_arguments.join(', ')}"
end

This will cleanly rescue any StandardErrors, and simply log them instead of raising an exception. I implemented this myself to quiet Rollbar.

Since it's a corner case, it's a good idea to test it as well:

spec/initializers/action_mailer_spec.rb:

RSpec.describe 'ActionMailer::DeliveryJob error recovery' do

   it 'should log the deserialization errors' do
     @user = create(:user)
     MyCustomMailer.send_something(@user).deliver_later
     @user.destroy

     expect(Rails.logger).to receive(:error).at_least(:once)
     Delayed::Worker.new.work_off
   end

end

Upvotes: 14

Related Questions