In a larger project, I am catching specific exceptions, adding some details to the message and then re-raising the same exception. Here is the basic idea of what I'm doing.
#!/usr/bin/env ruby
begin
puts 12 / 0
rescue ZeroDivisionError => e
raise e.exception("new message #{e.message}")
end
When I execute temp.rb from the command line, two exceptions are printed:
% ./temp.rb
./temp.rb:4:in `/': new message divided by 0 (ZeroDivisionError)
from ./temp.rb:4:in `<main>'
./temp.rb:4:in `/': divided by 0 (ZeroDivisionError)
from ./temp.rb:4:in `<main>'
I'm assuming that there is a stack or a list of exceptions somewhere and the exit process of Ruby prints out the entire list or stack of exceptions. I can see great benefits to this. But in my case, I'd like to get rid of the original exception from the list or stack. Is there a way to do this?
The "second" (original) exception is the cause of the rescued exception which is references by your new exception as it is created with Exception#exception.
Starting with Ruby 3.1, irb prints the details of the cause in addition to the actual exception to aid in debugging. Previous Ruby versions have ignored the cause here.
Thus, you can use other means to handle your exceptions rather than using Ruby's default handler of printing the details of an otherwise unhandled exception, e.g. by adding an explicit exception handler. Alternatively, you can also explicitly set the cause as you are creating your new exception:
begin
puts 12 / 0
rescue ZeroDivisionError => e
raise e.class, "new message #{e.message}", cause: nil
end
May be something like this to extend Exception with custom method
Exception.class_eval do
def add_message(msg)
mod =
Module.new do
define_method :to_s do
"#{super()} #{msg}"
end
end
extend mod
end
end
And then
begin
puts 12 / 0
rescue ZeroDivisionError => e
raise e.add_message("(even don't try it)")
end
In ruby you can resque, multiple excetions like this:
begin
...
rescue Exception1, Exception2
...
rescue Exception1
...
rescue Exception2
...
end
But I do not know how to raise multiple exceptions:
1] pry(main)> ? raise
From: eval.c (C Method):
Owner: Kernel
Visibility: private
Signature: raise(*arg1)
Number of lines: 13
With no arguments, raises the exception in $! or raises
a RuntimeError if $! is nil.
With a single String argument, raises a
RuntimeError with the string as a message. Otherwise,
the first parameter should be the name of an Exception
class (or an object that returns an Exception object when sent
an exception message). The optional second parameter sets the
message associated with the exception, and the third parameter is an
array of callback information. Exceptions are caught by the
rescue clause of begin...end blocks.
raise "Failed to create socket"
raise ArgumentError, "No parameters", caller
Or I cannot figure this in the raise doc
The purpouse of this is that I have an API call, this call tries to create an object in the API.
Then the APi could return all the problems in the object, from Activerecord Validators, So I can get thinks like that such as:
422 "The Item is not even","The Item needs too be bigger than 100"
422 "The Item is not even"
200 OK "Item created"
500 "I'm a tee pot
The idea is to capture this and raise exceptions like this
Begin
API CALL
rescue ItemnotEven,ItemnotBigger
do something
retry if
rescue ItemnotEven
retry if
rescue Connection error
Log cannot connect
end
Exceptions shouldn't be used for validations. Basically you shouldn't traverse the stack for validations in general.
What you fundamentally are doing is:
X is top level and can handle everything. X calls Y. Y calls Z. Z performs validations and does something after that, raising an exception if validation failed.
What you should be doing is:
X calls Y. Y calls V and X. V performs validations and returns result based on if the thing was valid. Y doesn't get to call X if V said the thing was invalid. Y propagates the invalidness or the successful result to X. X does what it would have done with if/else on the validity, rather than rescue.
But lets say you really want to do it. You should use throw/catch instead:
def validate_date(date)
errors = []
errors << 'Improper format' unless date.match?(/^\d{2}-\d{2}-\d{4}$/)
errors << 'Invalid day' unless date.match?(/^[0-3]\d/)
errors << 'Invalid month' unless date.match?(/-[12]\d-/)
errors << 'Invalid year' unless date.match?(/[12][90]\d{2}$/)
throw(:validation, errors) unless errors.empty?
end
def invoke_validation_and_do_stuff(date)
validate_date(date)
puts "I won't be called unless validation is successful for #{date}"
end
def meaningless_nesting(date)
invoke_validation_and_do_stuff(date)
end
def more_meaningless_nesting(date)
meaningless_nesting(date)
end
def top_level(date)
validation_errors = catch(:validation) do
more_meaningless_nesting(date)
nil
end
if validation_errors
puts validation_errors
else
puts 'Execution successful without errors'
end
end
top_level '20-10-2012'
# I won't be called unless validation is successful for 20-10-2012
# Execution successful without errors
top_level '55-50-2012'
# Invalid day
# Invalid month
I don't think you can raise multiple exceptions, it'll raise the first exception it finds and will be caught by the innermost rescue statement if multiple exist or depends on the type of exception you raise and type of rescue
No such concept exists, in any language that I am aware of.
You can raise a single exception consecutively, but not raising multiple exceptions at one time, and even if working on multiple threads to raise "simultaneously", it is still a single exception being raised on different control flows.
When an exception is raised, control flow goes to that exception. You have two options: do something about it, or crash. There is no third option, and no separate control flow popped up and is continuing on until this exception is dealt with accordingly.
If you want to see multiple failures as you stated in a comment, then you will still be doing it one at a time, as is how they would be raised. Exception gets raised, you inspect, log, do whatever, suppress it, and see the next if one gets raised for something else.
If you are asking how multiple unhandled exceptions can be raised, then it really doesn't make sense. It is akin to asking how to be in two places at once.
I'm trying to understand how errors propogate between classes in Ruby. I have this so far:
class User
def charge
puts "charging order soon"
raise RuntimeError.new("This is a runtime error")
rescue ArgumentError
puts "should never gets here"
end
end
class Runner
def run
begin
User.new.charge
rescue RuntimeError => e
puts e.message
end
end
end
Runner.new.run
When I run this, I get this which seems right:
$ ruby errors.rb
charging order soon
This is a runtime error
Inside runner, can I rescue from the RuntimeError with a specific message? If I have multiple RuntimeErrors being raised around my application, is there any way for the Runner's rescue clause to be raised only for RuntimeErrors with specific messages?
See https://stackoverflow.com/a/23771227/2981429
If you call raise inside a rescue block, the last raised exception will be re-raised.
In your exception block, you can check the message and choose to re-raise or not:
begin
User.new.charge
rescue RuntimeError => e
case e.message
when "This is a runtime error"
# put your handler code here
else
raise # re-raise the last exception
end
end
However if it's your goal to solely rescue errors that you yourself raise manually, then it's probably easier to define a custom error class instead:
class MyError < StandardError; end
Then instead of raise RuntimeError.new("message") use raise MyError.new("message"), and rescue it normally:
begin
User.new.charge
rescue MyError => e
# handler
end
This way you don't have to worry about your rescues interfering with the built-in exceptions.
I've read one tip for tagging your own exceptions in case you have your own library:
module AcmeHttp
module Error; end
end
begin
begin
raise IOError, "Some IO error"
rescue Exception => error
error.extend(AcmeHttp::Error)
raise
end
rescue AcmeHttp::Error => error
puts "Rescued AcmeHttp::Error: #{error}"
end
I'm not really sure how error.extend(AcmeHttp::Error) works and how later you can actually rescue using it. Also, is there some more elegant approach to tagging exceptions from your own library?
Object#extend adds the instance methods of one or more modules, to the receiver object.
So when you do error.extend(AcmeHttp::Error) you are actually adding the instance methods of the AcmeHttp::Error module to error which is an exception (actually a class object).
How do I add information to an exception message without changing its class in ruby?
The approach I'm currently using is
strings.each_with_index do |string, i|
begin
do_risky_operation(string)
rescue
raise $!.class, "Problem with string number #{i}: #{$!}"
end
end
Ideally, I would also like to preserve the backtrace.
Is there a better way?
To reraise the exception and modify the message, while preserving the exception class and its backtrace, simply do:
strings.each_with_index do |string, i|
begin
do_risky_operation(string)
rescue Exception => e
raise $!, "Problem with string number #{i}: #{$!}", $!.backtrace
end
end
Which will yield:
# RuntimeError: Problem with string number 0: Original error message here
# backtrace...
It's not much better, but you can just reraise the exception with a new message:
raise $!, "Problem with string number #{i}: #{$!}"
You can also get a modified exception object yourself with the exception method:
new_exception = $!.exception "Problem with string number #{i}: #{$!}"
raise new_exception
I realize I'm 6 years late to this party, but...I thought I understood Ruby error handling until this week and ran across this question. While the answers are useful, there is non-obvious (and undocumented) behavior that may be useful to future readers of this thread. All code was run under ruby v2.3.1.
#Andrew Grimm asks
How do I add information to an exception message without changing its class in ruby?
and then provides sample code:
raise $!.class, "Problem with string number #{i}: #{$!}"
I think it is critical to point out that this does NOT add information to the original error instance object, but instead raises a NEW error object with the same class.
#BoosterStage says
To reraise the exception and modify the message...
but again, the provided code
raise $!, "Problem with string number #{i}: #{$!}", $!.backtrace
will raise a new instance of whatever error class is referenced by $!, but it will not be the exact same instance as $!.
The difference between #Andrew Grimm's code and #BoosterStage's example is the fact that the first argument to #raise in the first case is a Class, whereas in the second case it is an instance of some (presumably) StandardError. The difference matters because the documentation for Kernel#raise says:
With a single String argument, raises a RuntimeError with the string as a message. Otherwise, the first parameter should be the name of an Exception class (or an object that returns an Exception object when sent an exception message).
If only one argument is given and it is an error object instance, that object will be raised IF that object's #exception method inherits or implements the default behavior defined in Exception#exception(string):
With no argument, or if the argument is the same as the receiver, return the receiver. Otherwise, create a new exception object of the same class as the receiver, but with a message equal to string.to_str.
As many would guess:
catch StandardError => e
raise $!
raises the same error referenced by $!, the same as simply calling:
catch StandardError => e
raise
but probably not for the reasons one might think. In this case, the call to raise is NOT just raising the object in $!...it raises the result of $!.exception(nil), which in this case happens to be $!.
To clarify this behavior, consider this toy code:
class TestError < StandardError
def initialize(message=nil)
puts 'initialize'
super
end
def exception(message=nil)
puts 'exception'
return self if message.nil? || message == self
super
end
end
Running it (this is the same as #Andrew Grimm's sample which I quoted above):
2.3.1 :071 > begin ; raise TestError, 'message' ; rescue => e ; puts e ; end
results in:
initialize
message
So a TestError was initialized, rescued, and had its message printed. So far so good. A second test (analogous to #BoosterStage's sample quoted above):
2.3.1 :073 > begin ; raise TestError.new('foo'), 'bar' ; rescue => e ; puts e ; end
The somewhat surprising results:
initialize
exception
bar
So a TestError was initialized with 'foo', but then #raise has called #exception on the first argument (an instance of TestError) and passed in the message of 'bar' to create a second instance of TestError, which is what ultimately gets raised.
TIL.
Also, like #Sim, I am very concerned about preserving any original backtrace context, but instead of implementing a custom error handler like his raise_with_new_message, Ruby's Exception#cause has my back: whenever I want to catch an error, wrap it in a domain-specific error and then raise that error, I still have the original backtrace available via #cause on the domain-specific error being raised.
The point of all this is that--like #Andrew Grimm--I want to raise errors with more context; specifically, I want to only raise domain-specific errors from certain points in my app that can have many network-related failure modes. Then my error reporting can be made to handle the domain errors at the top level of my app and I have all the context I need for logging/reporting by calling #cause recursively until I get to the "root cause".
I use something like this:
class BaseDomainError < StandardError
attr_reader :extra
def initialize(message = nil, extra = nil)
super(message)
#extra = extra
end
end
class ServerDomainError < BaseDomainError; end
Then if I am using something like Faraday to make calls to a remote REST service, I can wrap all possible errors into a domain-specific error and pass in extra info (which I believe is the original question of this thread):
class ServiceX
def initialize(foo)
#foo = foo
end
def get_data(args)
begin
# This method is not defined and calling it will raise an error
make_network_call_to_service_x(args)
rescue StandardError => e
raise ServerDomainError.new('error calling service x', binding)
end
end
end
Yeah, that's right: I literally just realized I can set the extra info to the current binding to grab all local vars defined at the time the ServerDomainError is instantiated/raised. This test code:
begin
ServiceX.new(:bar).get_data(a: 1, b: 2)
rescue
puts $!.extra.receiver
puts $!.extra.local_variables.join(', ')
puts $!.extra.local_variable_get(:args)
puts $!.extra.local_variable_get(:e)
puts eval('self.instance_variables', $!.extra)
puts eval('self.instance_variable_get(:#foo)', $!.extra)
end
will output:
#<ServiceX:0x00007f9b10c9ef48>
args, e
{:a=>1, :b=>2}
undefined method `make_network_call_to_service_x' for #<ServiceX:0x00007f9b10c9ef48 #foo=:bar>
#foo
bar
Now a Rails controller calling ServiceX doesn't particularly need to know that ServiceX is using Faraday (or gRPC, or anything else), it just makes the call and handles BaseDomainError. Again: for logging purposes, a single handler at the top level can recursively log all the #causes of any caught errors, and for any BaseDomainError instances in the error chain it can also log the extra values, potentially including the local variables pulled from the encapsulated binding(s).
I hope this tour has been as useful for others as it was for me. I learned a lot.
UPDATE: Skiptrace looks like it adds the bindings to Ruby errors.
Also, see this other post for info about how the implementation of Exception#exception will clone the object (copying instance variables).
Here's another way:
class Exception
def with_extra_message extra
exception "#{message} - #{extra}"
end
end
begin
1/0
rescue => e
raise e.with_extra_message "you fool"
end
# raises an exception "ZeroDivisionError: divided by 0 - you fool" with original backtrace
(revised to use the exception method internally, thanks #Chuck)
My approach would be to extend the rescued error with an anonymous module that extends the error's message method:
def make_extended_message(msg)
Module.new do
##msg = msg
def message
super + ##msg
end
end
end
begin
begin
raise "this is a test"
rescue
raise($!.extend(make_extended_message(" that has been extended")))
end
rescue
puts $! # just says "this is a test"
puts $!.message # says extended message
end
That way, you don't clobber any other information in the exception (i.e. its backtrace).
I put my vote that Ryan Heneise's answer should be the accepted one.
This is a common problem in complex applications and preserving the original backtrace is often critical so much so that we have a utility method in our ErrorHandling helper module for this.
One of the problems we discovered was that sometimes trying to generate more meaningful messages when a system is in a messed up state would result in exceptions being generated inside the exception handler itself which led us to harden our utility function as follows:
def raise_with_new_message(*args)
ex = args.first.kind_of?(Exception) ? args.shift : $!
msg = begin
sprintf args.shift, *args
rescue Exception => e
"internal error modifying exception message for #{ex}: #{e}"
end
raise ex, msg, ex.backtrace
end
When things go well
begin
1/0
rescue => e
raise_with_new_message "error dividing %d by %d: %s", 1, 0, e
end
you get a nicely modified message
ZeroDivisionError: error dividing 1 by 0: divided by 0
from (irb):19:in `/'
from (irb):19
from /Users/sim/.rvm/rubies/ruby-2.0.0-p247/bin/irb:16:in `<main>'
When things go badly
begin
1/0
rescue => e
# Oops, not passing enough arguments here...
raise_with_new_message "error dividing %d by %d: %s", e
end
you still don't lose track of the big picture
ZeroDivisionError: internal error modifying exception message for divided by 0: can't convert ZeroDivisionError into Integer
from (irb):25:in `/'
from (irb):25
from /Users/sim/.rvm/rubies/ruby-2.0.0-p247/bin/irb:16:in `<main>'
Here's what I ended up doing:
Exception.class_eval do
def prepend_message(message)
mod = Module.new do
define_method :to_s do
message + super()
end
end
self.extend mod
end
def append_message(message)
mod = Module.new do
define_method :to_s do
super() + message
end
end
self.extend mod
end
end
Examples:
strings = %w[a b c]
strings.each_with_index do |string, i|
begin
do_risky_operation(string)
rescue
raise $!.prepend_message "Problem with string number #{i}:"
end
end
=> NoMethodError: Problem with string number 0:undefined method `do_risky_operation' for main:Object
and:
pry(main)> exception = 0/0 rescue $!
=> #<ZeroDivisionError: divided by 0>
pry(main)> exception = exception.append_message('. With additional info!')
=> #<ZeroDivisionError: divided by 0. With additional info!>
pry(main)> exception.message
=> "divided by 0. With additional info!"
pry(main)> exception.to_s
=> "divided by 0. With additional info!"
pry(main)> exception.inspect
=> "#<ZeroDivisionError: divided by 0. With additional info!>"
This is similar to Mark Rushakoff's answer but:
Overrides to_s instead of message since by default message is defined as simply to_s (at least in Ruby 2.0 and 2.2 where I tested it)
Calls extend for you instead of making the caller do that extra step.
Uses define_method and a closure so that the local variable message can be referenced. When I tried using a class variable ##message, it warned, "warning: class variable access from toplevel" (See this question: "Since you're not creating a class with the class keyword, your class variable is being set on Object, not [your anonymous module]")
Features:
Easy to use
Reuses the same object (instead of creating a new instance of the class), so things like object identity, class, and backtrace are preserved
to_s, message, and inspect all respond appropriately
Can be used with an exception that is already stored in a variable; doesn't require you to re-raise anything (like the solution that involved passing the backtrace to raise: raise $!, …, $!.backtrace). This was important to me since the exception was passed in to my logging method, not something I had rescued myself.
Most of these answers are incredibly convoluted. Maybe they were necessary in Ruby 1.8 or whatever, but in modern versions* this is totally straightforward and intuitive. Just rescue => e, append to e.message, and raise.
begin
raise 'oops'
rescue => e
e.message << 'y daisy'
raise
end
Traceback (most recent call last):
4: from /Users/david/.rvm/rubies/ruby-2.7.2/bin/irb:23:in `<main>'
3: from /Users/david/.rvm/rubies/ruby-2.7.2/bin/irb:23:in `load'
2: from /Users/david/.rvm/rubies/ruby-2.7.2/lib/ruby/gems/2.7.0/gems/irb-1.2.6/exe/irb:11:in `<top (required)>'
1: from (irb):2
RuntimeError (oopsy daisy)
* I've only tested with 2.7.2 and 3.1.2, but I assume everything in between is covered, and probably some earlier versions of 2.x as well.
Another approach would be to add context (extra information) about the exception as a hash instead of as a string.
Check out this pull request where I proposed adding a few new methods to make it really easy to add extra context information to exceptions, like this:
begin
…
User.find_each do |user|
reraise_with_context(user: user) do
send_reminder_email(user)
end
end
…
rescue
# $!.context[:user], etc. is available here
report_error $!, $!.context
end
or even:
User.find_each.reraise_with_context do |user|
send_reminder_email(user)
end
The nice thing about this approach is that it lets you add extra information in a very concise way. And it doesn't even require you to define new exception classes inside which to wrap the original exceptions.
As much as I like #Lemon Cat's answer for many reasons, and it's certainly appropriate for some cases, I feel like if what you are actually trying to do is attach additional information about the original exception, it seems preferable to just attach it directly to that exception it pertains to rather than inventing a new wrapper exception (and adding another layer of indirection).
Another example:
class ServiceX
def get_data(args)
reraise_with_context(StandardError, binding: binding, service: self.class, callee: __callee__) do
# This method is not defined and calling it will raise an error
make_network_call_to_service_x(args)
end
end
end
The downside of this approach is that you have to update your error handling to actually use the information that may be available in exception.context. But you would have to do that anyway in order to recursively call cause to get to the root excetion.
It's possible to use :cause key to prevent message duplication
The cause of the generated exception (accessible via Exception#cause) is automatically set to the "current" exception ($!), if any. An alternative value, either an Exception object or nil, can be specified via the :cause argument.
begin
do_risky_operation
rescue => e
raise e.class, "#{e.message} (some extra message)", e.backtrace, cause: nil
end