Learnings about wrapped exceptions in Ruby using the built-in Exception#cause
Starting in Ruby 2.1, Ruby has Exception#cause
which wraps the previous exception. This is typically referred to as a “wrapped exception.” Before Ruby 2.1, many projects would add a similar feature on a per-error basis. (If you happen to be on a version of Ruby before 2.1, there’s a backport gem which adds the feature… but please try to upgrade.)
This is now widely supported and defaults to $!
. It should generally work in error reporting applications like Bugsnag, which renders the cause
as “caused by” in the stacktrace tab.
Finally, unlike Java, which allows a nested exception’s cause to be set manually, Ruby only allows #cause
to be $!
at the time that the wrapping exception is raised. There is no #cause=
or argument to Exception.new
, and $!
is a read-only variable. See the feature in Ruby’s Redmine for discussion about the reasoning. In effect, if you want to wrap an exception, you have to re-raise. This makes using any kind of Exception
(StandardError
, etc) as a value object more difficult. This includes use of Exception#cause
in tests/specs or as a return value when you’d like to avoid the overhead of yet another raise
.
Overall, Exception#cause
is a great addition to base Ruby, but I would love an option like initCause
in Java.
Charles Nutter said this:
Since the base #cause and $! logic made it in, perhaps we should call this bug closed as of 2.1 and add a new bug for additional ways to initialize the exception cause.
…but I’m not aware of any work to allow @cause
to be manually set.