Does Ruby support exception wrapping (exception chaining)?

I could not find any information about this, except regarding DRb.

It seems like the raise method / Exception class ought to allow me to
pass in a ‘causal’ exception, e.g.

begin
foo
rescue Exception => e
raise ServiceNotFoundException, “The service could not be contacted”,
e
end

This doesn’t work. I can, however, pass in the backtrace, e.g.

raise ServiceNotFoundException, “The service could not be contacted”,
e.backtrace

However, doing this I get the stack trace but not the type or message
from the causal exception (unless I manually put them in the message
string). Ideally, I’d be able to use exception chaining such as in
Java, in which my stack traces include causal exceptions:

WebServiceCommunicationException: The WSDL could not be obtained from
http://blah/wsdl.
from (webservice_client.rb):123:in obtain_wsdl' from (webservice_client.rb):12:inrequest_price’
caused by ParsingError: Unexpected element ‘html’
from (other_class.rb):234 in ‘some_method’
from (other_class.rb):23 in ‘some_other_method’

including a ‘cause’ field on the Exception class, e.g. e.cause.

Is there some reason why this would not fit the Ruby style? If not, is
this something that would be considered as a patch/change for Ruby?

I realize I could probably monkeypatch Exception, but this seems (to me)
like it would be an improvement to the language.

Thanks,

Brian H.


This email may contain material confidential to
Pearson. If you were not an intended recipient,
please notify the sender and delete all copies.
We may monitor email to and from our network.


Hartin, Brian wrote:

end
The second arg to raise (i.e. the message) can be any object:

class ServiceNotFoundException < StandardError; end

begin
begin
raise “foo”
rescue Exception => e
raise ServiceNotFoundException, [“The service could not be
contacted”,
e]
end
rescue Exception => e_outer
puts e_outer
p e_outer.message
end

END

Output:

#ServiceNotFoundException:0xb7d9b75c
[“The service could not be contacted”, #<RuntimeError: foo>]

Brian H. wrote:

Joel,

This doesn’t really provide the main benefit to exception wrapping
mechanism: the ability to wrap low-level exceptions in an appropriate
higher-level exception and yet not lose the entire stack trace, or the
types and messages of the low-level exceptions. Exception wrapping
(a.k.a. chaining) in Java (a good model for this feature, I think) makes
the idea of a ‘causal Exception’ part of the class (via a ‘cause’
field), and the stack trace: an Exception’s stack trace includes the
causal ‘chain’ of Exceptions automatically.

How about this?

class ServiceNotFoundException < StandardError
attr_reader :cause
def initialize cause
@cause = cause
end
end

begin
begin
raise “foo”
rescue Exception => e
e2 = ServiceNotFoundException.new e
raise e2, “The service could not be contacted: #{e.message}”,
e.backtrace
end
rescue Exception => e_outer
puts e_outer
p e_outer.cause
puts e_outer.backtrace.join("\n ")
end

END

Output:

The service could not be contacted: foo
#<RuntimeError: foo>
wrapped-exceptions.rb:10

Joel VanderWerf wrote:

includes the causal ‘chain’ of Exceptions automatically.
Here’s an alternative, perhaps with too much magic:

class ServiceNotFoundException < StandardError
attr_reader :cause
def initialize cause
@cause = cause
end

DEFAULT_MESSAGE = “The service could not be contacted”

def self.[](e, msg = DEFAULT_MESSAGE)
e2 = new(e)
raise e2, “#{msg}: #{e.message}”, e.backtrace
end
end

begin
begin
raise “foo”
rescue Exception => e
ServiceNotFoundException[e]
end
rescue Exception => e_outer
puts e_outer
p e_outer.cause
puts e_outer.backtrace.join("\n ")
end

END

Output:

The service could not be contacted: foo
#<RuntimeError: foo>
wrapped-exceptions.rb:17

Joel,

This doesn’t really provide the main benefit to exception wrapping
mechanism: the ability to wrap low-level exceptions in an appropriate
higher-level exception and yet not lose the entire stack trace, or the
types and messages of the low-level exceptions. Exception wrapping
(a.k.a. chaining) in Java (a good model for this feature, I think) makes
the idea of a ‘causal Exception’ part of the class (via a ‘cause’
field), and the stack trace: an Exception’s stack trace includes the
causal ‘chain’ of Exceptions automatically.

I think I can write this up for my own use. I just wondered if I was
missing an existing feature.

Thanks for the response!

Joel VanderWerf wrote:

Hartin, Brian wrote:

end
The second arg to raise (i.e. the message) can be any object:

class ServiceNotFoundException < StandardError; end

begin
begin
raise “foo”
rescue Exception => e
raise ServiceNotFoundException, [“The service could not be
contacted”,
e]
end
rescue Exception => e_outer
puts e_outer
p e_outer.message
end

END

Output:

#ServiceNotFoundException:0xb7d9b75c
[“The service could not be contacted”, #<RuntimeError: foo>]

Hi guys

I came up with this based on your ideas but it doesn’t have the
complexity:

class ParseFailed < Exception ; end

rescue Exception => e
raise ParseFailed.new("Page generation failed #{e}: 

#{e.backtrace.join("\n")}")

This raises my named exception with the text and full backtrace of the
one thrown as the message. Not as good as chaining Exception.new(e) but
it allows you to see the trace in the message without having to do
another backtrace.

It met my needs and seems a little simpler than hacking another
exception class.

On Jan 24, 2011, at 19:23, Ryan H. wrote:

To reraise the error and pass the original backtrace:

begin
1/0
rescue Exception => e
raise $!, “Oh dear! #{$!}”, $!.backtrace
end

$ ri Exception.set_backtrace
Exception.set_backtrace

(from ruby core)

exc.set_backtrace(array) -> array

To reraise the error and pass the original backtrace:

begin
1/0
rescue Exception => e
raise $!, “Oh dear! #{$!}”, $!.backtrace
end

On Tue, Apr 1, 2008 at 7:21 PM, Hartin, Brian [email protected]
wrote:

I could not find any information about this, except regarding DRb.

It seems like the raise method / Exception class ought to allow me to
pass in a ‘causal’ exception, e.g.

Why? This is a feature most needed by languages with checked
exceptions.

e.backtrace
caused by ParsingError: Unexpected element ‘html’
from (other_class.rb):234 in ‘some_method’
from (other_class.rb):23 in ‘some_other_method’

including a ‘cause’ field on the Exception class, e.g. e.cause.

Is there some reason why this would not fit the Ruby style? If not, is
this something that would be considered as a patch/change for Ruby?

Ruby does not have checked exceptions and it provides enough
mechanisms to deal with exceptions which make wrapping superfluous
(see below).

I realize I could probably monkeypatch Exception, but this seems (to me)
like it would be an improvement to the language.

It may be - but not a big one. I would not do it.

If you cannot handle the exception, don’t catch it => no need for
wrapping. If you need to cleanup in all cases you can use “ensure”
(like “finally” in Java) => no need for wrapping exceptions either.
If you need to do some cleanup in case of exception only, you can use
“raise” without arguments:

10:31:20 ~$ ruby19 <<CODE

g
rescue Exception => e
printf “Main caught %p\n”, e
puts e.backtrace
end
CODE
Caught #<RuntimeError: Ex 1>
Main caught #<RuntimeError: Ex 1>
-:2:in f' -:5:ing’
-:11:in `’
10:32:53 ~$

DRb really seems the only place where a kind of wrapping may make
sense but even here you probably do not want to wrap exceptions but
just carry over backtraces, because the exception class may not be
known to the client.

Kind regards

robert

Robert K. wrote in post #977344:

DRb really seems the only place where a kind of wrapping may make
sense but even here you probably do not want to wrap exceptions but
just carry over backtraces, because the exception class may not be
known to the client.

An example of where this can be infinitely useful is in parsing a CSV or
Excel file, or iterating over any collection of data. Often a tuple will
trigger an exception, and it’s useful to know on which line the problem
occurred.

tuples.each_with_index do |tuple, i|
begin
Something.create(tuple)
rescue Exception => e
raise $!, “#{$!} on row #{i}”, $!.backtrace
end
end

This would give you an error like:

“NoMethodError: undefined method `foobar’ for nil:NilClass on row 123”

…which is very helpful when tracking down data processing errors.

Notice that it still raises the original error, including the original
error class, and the original backtrace. Only the message has changed.

On Tue, Jan 25, 2011 at 4:56 PM, Ryan H. [email protected]
wrote:

tuples.each_with_index do |tuple, i|
begin
Something.create(tuple)
rescue Exception => e
raise $!, “#{$!} on row #{i}”, $!.backtrace

I’d rather use e instead of $! since we have it already:

raise e, “#{e} on row #{i}”, e.backtrace

error class, and the original backtrace. Only the message has changed.
In this case I would chose one of these two approaches:

  1. output the original error along with meta data

error_count = 0 # in case we need this information

tuples.each_with_index do |tuple, i|
begin
Something.create(tuple)
rescue Exception => e
$stderr.puts “Error with processing tuple #{i}: #{tuple.inspect}”,
e.backtrace
error_count += 1
end
end

  1. Create a custom error class that carries all information needed.

class DataError < StandardError
attr_reader :tuple, :meta_data, :source_error

def initialize(tuple, meta_data = nil, source_error = $!)
@tuple = tuple
@meta_data = meta_data
@source_error = source_error
end
end

tuples.each_with_index do |tuple, i|
begin
Something.create(tuple)
rescue Exception => e
raise DataError.new(tuple, i, e), “Error with tuple #{i}”
end
end

Whether I’d pick 1 or 2 would largely depend on the size of the
application and whether I need structured handling of those errors.

Kind regards

robert

Robert K. wrote in post #977344:

Ruby does not have checked exceptions and it provides enough
mechanisms to deal with exceptions which make wrapping superfluous
(see below).
[…]

It’s very common to want to catch an exception and wrap it in a custom
class, so you can add some information that is only available locally
(e.g. parameter values that are probably responsible for the exception).
That problem is independent of using checked exceptions and has to do
with the general problem of propagating information up an unwinding
stack. The way Java allows this is very helpful.

DRb really seems the only place where a kind of wrapping may make
sense but even here you probably do not want to wrap exceptions but
just carry over backtraces, because the exception class may not be
known to the client.

DRb is just one of many situations where you want to raise a new
exception while retaining all information from the old one. The main
difference with DRb is that, as you say, you often don’t want to retain
the old object, but only the contents of that object.

On Fri, Feb 18, 2011 at 4:55 PM, Ivo W. [email protected] wrote:

That problem is independent of using checked exceptions and has to do
with the general problem of propagating information up an unwinding
stack. The way Java allows this is very helpful.

Is it really that common? It may seem so on first sight but I can’t
remember having ever felt the need for this in Ruby. This may have to
do with my usage patterns of the language but I feel that you usually
deal with exceptions only by either logging the message or rolling
back some other code’s work.

Cheers

robert

On Tue, Apr 1, 2008 at 10:21 AM, Hartin, Brian
[email protected] wrote:

end

Is there some reason why this would not fit the Ruby style?

You can certainly do a custom exception class that carries information
about a preceding triggering exception; the default handler won’t do
anything with that information, though, but if you handle the
exception yourself, you can include the information with your output.
You can even include a method in your custom exception class to dump
the info so that client code can just call the method to get the error
report.
If not, is

On Mon, Feb 21, 2011 at 4:31 AM, Robert K. > Is it really that
common? It may seem so on first sight but I can’t

remember having ever felt the need for this in Ruby. This may have to
do with my usage patterns of the language but I feel that you usually
deal with exceptions only by either logging the message or rolling
back some other code’s work.

Libraries which throw away the original exception and substitute their
own have caused me no end of grief. I recommend introducing an
“original” attribute in library exception classes which can be used to
point to the original exception that was replaced. It is even possible
to automatically populate this attribute:

class MyError < StandardError
attr_reader :original
def initialize(msg, original=$!)
super(msg)
@original = original;
end
end

Cheers,

There is actually a library implementing this kind of exception
handling. I haven’t used it personally but looks OK. In one of my
projects where I needed causes on exceptions I made it “manual” way.

https://github.com/loganb/nestegg

I think that causes in exceptions do you no harm except longer stack
messages. Having them in place changes one more thing there when
catching exception and throwing new to have some additional information
one is not obligated to think “what information I’m loosing here”. This
adds some DRY to the code so I like it.

Cheers
Avdi G. wrote in post #982947:

On Mon, Feb 21, 2011 at 4:31 AM, Robert K. > Is it really that
common? It may seem so on first sight but I can’t

remember having ever felt the need for this in Ruby. This may have to
do with my usage patterns of the language but I feel that you usually
deal with exceptions only by either logging the message or rolling
back some other code’s work.

Libraries which throw away the original exception and substitute their
own have caused me no end of grief. I recommend introducing an
“original” attribute in library exception classes which can be used to
point to the original exception that was replaced. It is even possible
to automatically populate this attribute:

class MyError < StandardError
attr_reader :original
def initialize(msg, original=$!)
super(msg)
@original = original;
end
end

Cheers,

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs