Do not swallow exceptions

This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the exceptions category.

Last Updated: 2024-11-23

I had failing tests for a bulk email operation, but nothing seemed to appear in the logs and no exceptions were thrown. After much searching, I found this lump of code:

class UpsellBulkOperation
  def each
    collection.each do |user|
        yield user
      end

    successful << user.email
    # Here be dragons:
    rescue StandardError
      unsuccessful << user.email
    end

    { successful: successful, unsuccessful: unsuccessful }
  end
end

The issue was that the exceptions were swallowed. There was a somewhat good intention here, in that we didn't want the bulk email operation to fail to send emails to other customers. But this resilience made it difficult to debug. Some logging would have helped:

class UpsellBulkOperation
  def each
    collection.each do |user|
        yield user
      end
      successful << user.email
    rescue StandardError => error
      Rails.logger.error("ERROR: Could not send email to #{user.email}:\n  #{error}")
      unsuccessful << user.email
    end

    { successful: successful, unsuccessful: unsuccessful }
  end
end

Another example of swallowing errors

I discovered that I was inadvertently hiding exceptions in a production environment, with the result that I didn't receive exception notifications about certain actual errors.

In my web server, this happened with high-level wrapping exception handlers:

rescue_from StandardError do |error|
  # !!! All info about exception lost for the developers
  render 'errors/server_error', status: 500
end

I repaired this by intercepting with a call to Rollbar:

rescue_from StandardError do |error|
  Rollbar.error(error) # fixed
  render 'errors/server_error', status: 500
end

An example from JavaScript

In my Javsacript client, this mistake manifested within error handlers:

{
    onError: function (err) {
      // All info about exception lost for the developers
      showErrorMessage(
        `PayaPal return an error code during payment. Please try again or contact our customer support`
      )
    }
}

The fix was to intercept and send the error to Rollbar first:

{
    onError: function (err) {
    Rollbar.error("Error with PayPal payment", err)
      showErrorMessage(
        `PayaPal return an error code during payment. Please try again or contact our customer support`
      )
    }
}