26

The problem is how can I catch exception in delivering mail by ActionMailer. For me it sounds impossible, because in this case ActionMailer should sent mail to mailserver, and if mailserver returns error, ActionMailer should show me this error. I am interested only in counting undelivered mails.

Do you have any ideas how to implement this? Thanks!

com
  • 2,606
  • 6
  • 29
  • 44

5 Answers5

31

I'm using something like this in the controller:

 if @user.save
      begin
      UserMailer.welcome_email(@user).deliver
      flash[:success] = "#{@user.name} created"
      rescue Net::SMTPAuthenticationError, Net::SMTPServerBusy, Net::SMTPSyntaxError, Net::SMTPFatalError, Net::SMTPUnknownError => e
        flash[:success] = "Utente #{@user.name} creato. Problems sending mail"
      end
      redirect_to "/"
piffy
  • 723
  • 2
  • 8
  • 21
13

This should work in any of your environment files. development.rb or production.rb

config.action_mailer.raise_delivery_errors = true
Alexey Shein
  • 7,342
  • 1
  • 25
  • 38
Sukeerthi Adiga
  • 531
  • 5
  • 9
  • I don't want to show the exception to user, I need just identification that delivery failed – com Jan 03 '12 at 12:22
  • 2
    The user won't see the exception as long as you rescue and handle it! I also like to report things to Airbrake or some exception notification service so that we know if things are failing—even if they're being handled pleasantly. –  Mar 21 '14 at 14:35
11
class ApplicationMailer < ActionMailer::Base
  rescue_from [ExceptionThatShouldBeRescued] do |exception|
    #handle it here
  end
end

Works with rails 5 onwards. And it's the best practice.

Sukeerthi Adiga
  • 531
  • 5
  • 9
10

If you are sending a lot of emails, you can also keep your code more DRY and get notifications of exceptions to your email by doing something like this:

status = Utility.try_delivering_email do
  ClientMailer.signup_confirmation(@client).deliver
end

unless status
  flash.now[:error] = "Something went wrong when we tried sending you and email :("
end

Utility Class:

class Utility
  # Logs and emails exception
  # Optional args:
  # request: request Used for the ExceptionNotifier
  # info: "A descriptive messsage"
  def self.log_exception(e, args = {})
    extra_info = args[:info]

    Rails.logger.error extra_info if extra_info
    Rails.logger.error e.message
    st = e.backtrace.join("\n")
    Rails.logger.error st

    extra_info ||= "<NO DETAILS>"
    request = args[:request]
    env = request ? request.env : {}
    ExceptionNotifier::Notifier.exception_notification(env, e, :data => {:message => "Exception: #{extra_info}"}).deliver
  end

  def self.try_delivering_email(options = {}, &block)
    begin
      yield
      return true
    rescue  EOFError,
            IOError,
            TimeoutError,
            Errno::ECONNRESET,
            Errno::ECONNABORTED,
            Errno::EPIPE,
            Errno::ETIMEDOUT,
            Net::SMTPAuthenticationError,
            Net::SMTPServerBusy,
            Net::SMTPSyntaxError,
            Net::SMTPUnknownError,
            OpenSSL::SSL::SSLError => e
      log_exception(e, options)
      return false
    end
  end
end

Got my original inspiration from here: http://www.railsonmaui.com/blog/2013/05/08/strategies-for-rails-logging-and-error-handling/

Michael Yagudaev
  • 6,049
  • 3
  • 48
  • 53
10

This is inspired by the answer given by @sukeerthi-adiga

class ApplicationMailer < ActionMailer::Base
  # Add other exceptions you want rescued in the array below
  ERRORS_TO_RESCUE = [
    Net::SMTPAuthenticationError,
    Net::SMTPServerBusy,
    Net::SMTPSyntaxError,
    Net::SMTPFatalError,
    Net::SMTPUnknownError,
    Errno::ECONNREFUSED
  ]

  rescue_from *ERRORS_TO_RESCUE do |exception|
    # Handle it here
    Rails.logger.error("failed to send email")
  end

end

The * is the splat operator. It expands an Array into a list of arguments.

More explanation about (*) splat operator

Marvin Kang'
  • 198
  • 1
  • 6