Error handling and reporting with Rails

Updated . Posted . Visible to the public.

Dealing with exceptions in a Rails application is (surprisingly) complex. There are multiple configurations and methods that influence how exceptions are actually handled. This card explains some of the interactions between them.

In general, whenever an exception is raised in your app you want the following things to be true:

  • Your server should not crash (your Ruby process should continue to run)
  • Your user should receive an appropiate response
  • If the exception is somehow actionable for you, you should receive a notification about the exception
  • Exceptions you can do nothing about should not create noise in your exception tracker
  • For debugging purposes it should be possible to see the stacktrace of the exception

Rails' error handling default

Rails' last resort for exceptions that noone else handled is called ExceptionApp. It's configurable Show archive.org snapshot via config.exceptions_app and by default it's set to ActionDispatch::PublicExceptions ( documentation Show archive.org snapshot ) called from the ActionDispath::ShowExceptions middleware Show archive.org snapshot .

For HTML requests, ActionDispatch::PublicExceptions will render html files public/<status_code>.html.
For other content types, it will convert a simple hash { error: '404', status: 'Not Found' } with to_json for application/json requests or application/xml with to_xml.

Mapping unhandled exceptions to a status code

ActionDispatch::PublicExceptions uses the exception's name to map to appropiate status codes. This mapping is configurable with ActionDispatch::ExceptionWrapper.rescue_responses:

{
  "AbtractController::ActionNotFound" => :not_found,
  "ActiveRecord::RecordInvalid" => :unprocessable_content,
  ...
}

This is the reason why by default, if you don't rescue ActiveRecord::RecordInvalid yourself in production, Rails will not crash but map the exception to a status (:unprocessable_content/422) and respond with public/422.html.

If this behaviour is good enough for you, you can just modifiy ActionDispatch::ExceptionWrapper.rescue_responses and everything will work fine. You can also map the responses via config.action_dispatch.rescue_responses ( documentation Show archive.org snapshot ).

Warning

Make sure you're not adding exceptions you want to be notified about to rescue_responses. Rails will not report these exceptions by default.

Adding debugging information

In development only, there's an additional middleware ActionDispatch::DebugExceptions ( documentation Show archive.org snapshot ) before ActionDispatch::PublicExceptions. It will show something completely different than ActionDispatch::PublicExceptions based on several configurations:

  • config.show_exceptions can't be set to :none.

and

or

  • :show_detailed_exception? needs to return true. It is populated by consider_all_requests_local or can be overwritten per controller when consider_all_requests_local is set to false.

For HTML requests, DebugExceptions will respond with the default red Rails error page that everyone knows.
For other content types, the configuration Show archive.org snapshot config.debug_exception_response_format needs to be set to :api, and then Rails will respond to non-html requests with a similar hash conversion as PublicExceptions, { error: ..., status: ..., exception: ..., traces: ... }.to_<json/xml>

Notifying about exceptions with Error Reporter and Sentry

Rails' provides a mechanism to report exceptions called Error Reporter Show archive.org snapshot . If you want Sentry to subscribe to the error reporter, please set config.rails.register_error_subscriber to true.

By default the Rails Error Reporter, will not report exceptions configured in ExceptionWrapper.rescue_responses.

Warning

This is different than Sentry's default, which will report these exceptions.
Use config.rails.report_rescued_exceptions = false to align behaviours.

Customizing Error Responses

We have a card on customizing error pages. Follow that one if you want to fully control error responses in production through the default PublicExceptions exceptions app.

Testing exceptions

Exception handling behaves differently in the test environment: since Rails 7.2 config.action_dispatch.show_exceptions defaults to :rescuable ( documentation Show archive.org snapshot ), so your specs see the mapped response from rescue_responses instead of the raw exception.

The ActionDispatch::ShowExceptions middleware sits in the stack in every environment (unlike the dev-only DebugExceptions) and reads this setting to decide whether to re-raise or render:

  • :none re-raises every exception (the old test default, which is why expect { }.to raise_exception used to pass).
  • :rescuable renders the mapped response only for exceptions listed in rescue_responses and re-raises the rest, so you keep a stack trace for unexpected errors.
  • :all renders a response for every exception.

See our card on testing exceptions and the rescue_responses setting.

Profile picture of Niklas Hä.
Niklas Hä.
Last edit
Felix Eschey
License
Source code in this card is licensed under the MIT License.
Posted by Niklas Hä. to makandra dev (2026-06-25 14:00)