Rescue error ruby

A raised exception can be rescued to prevent it from crashing your application once it reaches to top of the call stack. In this article, we'll examine different ways to rescue exceptions.

A raised exception can be rescued to prevent it from crashing your application once it reaches the top of the call stack. In Ruby, we use the rescue keyword for that.

When rescuing an exception in Ruby, you can specify a specific error class that should be rescued from.

begin
  raise 'This exception will be rescued!'
rescue StandardError => e
  puts "Rescued: #{e.inspect}"
end

Note: When using raise without specifying an exception class, Ruby will default to RuntimeError.

Besides specifying a single exception class to rescue, you can pass an array of exception classes to the rescue keyword. This will allow you to respond to multiple errors in the same way.

begin
  raise 'This exception will be rescued!'
rescue StandardError, AnotherError => e
  puts "Rescued: #{e.inspect}"
end

Multiple rescue blocks can be used to handle different errors in different ways. This can be useful when working with a library that produces different exceptions for different scenarios.

begin
  raise 'This exception will be rescued!'
rescue StandardError => e
  puts "Rescued: #{e.inspect}"
rescue AnotherError => e
  puts "Rescued, but with a different block: #{e.inspect}"
end

The exception hierarchy

Ruby’s exception hierarchy is used to differentiate between different types of errors, while giving you the ability to rescue from a group of errors without specifying all of them.

Although libraries can define their own exception subclasses, the list of built-in exception subclasses on Ruby 2.5 looks like this:

- NoMemoryError
- ScriptError
    - LoadError
    - NotImplementedError
    - SyntaxError
- SecurityError
- SignalException
    - Interrupt
- StandardError (default for `rescue`)
    - ArgumentError
        - UncaughtThrowError
    - EncodingError
    - FiberError
    - IOError
        - EOFError
    - IndexError
        - KeyError
        - StopIteration
    - LocalJumpError
    - NameError
        - NoMethodError
    - RangeError
        - FloatDomainError
    - RegexpError
    - RuntimeError (default for `raise`)
    - SystemCallError
        - Errno::*
    - ThreadError
    - TypeError
    - ZeroDivisionError
- SystemExit
- SystemStackError
- fatal (impossible to rescue)

When omitting the exception class in a rescue block, StandardError is assumed. Because ArgumentError and NoMethodError are subclasses of StandardError, these are rescued from when they occur in the block.

A good example of how the exception hierarchy works is SystemCallError, which is a low-level platform-dependent exception class. It’s seen most often when reading or writing to files.

Ruby’s File.read method will raise an exception if it fails to read the file. That can happen because of a number of reasons, like the file not existing or the program not having the correct permissions to read it.

Since these problems are platform-dependent, Ruby can raise different exceptions depending on what kind of operating system is running on the machine. For low-level errors like this, Ruby implements a different list of Errno::*-exceptions for each platform.

All of these Errno::* exceptions are subclasses of SystemCallError. Although they’re platform-specific, they can still be used in a rescue block by rescuing from SystemCallError.

begin
  File.read("does/not/exist")
rescue SystemCallError => e
  puts "Rescued: #{e.inspect}"
end

Swallowing exceptions

Usually, it’s best to be as specific as possible when rescuing exceptions, to prevent unintentionally swallowing exceptions.

image = nil
 
begin
  File.read(image.filename)
rescue
  puts "File can't be read!"
end

In this example, the image variable is nil, so it raises a NoMethodError when we try to call #filename on it (NoMethodError: undefined method `filename' for nil:NilClass). Because every StandardError subclass is rescued from (including NoMethodError), the exception is swallowed and the “File can’t be read!”-message is printed. This hides a possible bug in the code.

Note: Although it’s possible, using the Exception superclass in a rescue block is highly discouraged.

Have any questions about raising or rescuing exceptions in Ruby? Please don’t hesitate to let us know at @AppSignal. Of course, we’d love to know how you liked this article, or if you have another subject you’d like to know more about.

A common problem in Ruby is that you get error messages, which in technical terms we call “exceptions”.

These exceptions can be expected, like a file that may be available sometimes but missing in others, or an API that is only available temporarily due to some restrictions, or they can be unexpected.

Today you’ll learn to manage expected errors.

How?

Well, let me introduce you to “begin” & “rescue” in Ruby, two important keywords used for handling error conditions.

How do they work?

First, you need to understand something.

Your Ruby programs can trigger an error at multiple points while they’re running.

Examples include:

  • Trying to read a non-existing file.
  • Dividing a number by zero.
  • A web server you’re working with has an outdated SSL certificate.

When an error happens… Ruby doesn’t crash right away!

You get a chance to recover from the error. We call this “exception handling”.

Ruby gives you a few keywords to implement error recovery in your code. These keywords are begin & rescue.

Let’s discover how to use them!

How to Handle Ruby Exceptions

How do you handle these exceptions?

You can wrap the code that raises the exception with a begin / rescue block.

Here’s how it works…

The first section (begin), has the code that you’re going to run & that may raise an exception.

Example:

begin
  IO.sysopen('/dev/null')
rescue
  # ...
end

Here we’re trying to open a file with sysopen. An exception is raised if we can’t open the file.

This is the perfect time to use the rescue keyword!

Using this keyword you can say what you want to happen when an exception is raised. So the failure mode is under your control.

Example:

begin
  IO.sysopen('/dev/null')
rescue
  puts "Can't open IO device."
end

You want to log this error & maybe provide some kind of default value.

Don’t. Ignore. Errors.

Rescuing Multiple Exceptions

You need to know that rescue takes an optional argument.

What is this argument?

This argument is the exception class that you want to rescue from.

It depends on what code you’re running.

For IO:

  • This may be Errno::ENOENT for a missing file
  • Or Errno::EACCES for a permission error

The best part?

You can handle multiple exceptions in the same begin/rescue block.

Like this:

begin
  IO.sysopen('/dev/null')
rescue Errno::ENOENT
  puts "File not found."
rescue Errno::EACCES
  puts "Insufficient permissions, not allowed to open file."
end

If you want to have the same action happen for multiple exceptions…

You can do this:

begin
  IO.sysopen('/dev/null')
rescue Errno::ENOENT, Errno::EACCES
  puts "There was an error opening the file."
end

Let’s keep learning!

How to Rescue Exceptions Inside Blocks & Methods

You don’t always have to use the begin keyword.

There are cases where you can leave it out.

Where?

Inside methods & blocks.

Example:

def get_null_device
  IO.sysopen('/dev/null')
rescue Errno::ENOENT
  puts "Can't open IO device."
end

The method definition itself does the work of begin, so you can omit it.

You can also do this with blocks.

Example:

["a.txt", "b.txt", "c.txt"].map do |f|
  IO.sysopen(f)
rescue Errno::ENOENT
  puts "Can't open IO device: #{f}."
end

Now, there is one more way to use the rescue keyword without begin.

Let’s see how that works.

Understanding Inline Rescue & Why Is It Dangerous

You can use rescue inline.

In a few rare scenarios, you may find this form of exception handling useful.

Here’s an example:

["a.txt", "b.txt", "c.txt"].select { |f| File.open(f) rescue nil }.map(&:size)

This allows you to open only the files that exist & ignore those that don’t.

As a result, you get the size for the existing files.

Without exceptions being raised.

Why do this?

Well, it lets you keep your code all in one line.

That’s about it.

There’s a “hidden danger” when using this form of rescue because you’re getting all exceptions descending from StandardError.

Which are most exceptions.

Why is that NOT good?

Because it’s best to only handle specific exceptions, instead of a broad selection of them.

This avoids hiding errors from yourself!

Hidden errors can lead to all kinds of strange behavior & hard-to-debug issues.

Summary

You’ve learned about errors in Ruby, basic exception handling & the rescue / begin keywords.

Please share this article if you found it helpful 🙂

Thanks for reading!

In the Ruby programming language, exceptions are a common feature. About 30 different exception subclasses are defined in the Ruby standard library, some of which have their subclasses. Ruby’s exception mechanism is quite strong, yet it is frequently misused.

This article will go over how to use rescue exceptions in ruby and how to handle them.

Here’s how it is done:

  1. What is an Exception?
  2. What is Exception Handling?
  3. When to Handle an Exception?
  4. When do Exceptions Occur?
  5. Exception handling in Ruby
  6. Be Specific About Your Rescue
  7. The begin-rescue

What is an Exception?

A program error condition is represented by an exception. Exceptions are a mechanism for suspending a program’s execution. They work in the same way as «break» in that they cause the instruction pointer to jump to a different location. The location, unlike break, could be another tier of the program stack. Ruby programs crash when unhandled exceptions occur.

What is Exception Handling?

Software systems are typically prone to errors. Exceptions are more likely to occur in systems that include human involvement because they attract errors on numerous levels.

Syntactical errors, network errors, form input errors, invalid authentication errors, and so on are all examples of errors. These can damage user experience and potentially emerge as security loopholes, allowing attackers to compromise the system if not addressed.

Exception handling prevents system failures and saves the user from annoying error messages.

When an error condition occurs, the programming language usually compels the program to terminate, and control of the program is lost. Exception handling allows you to maintain control and respond to what occurs after an error has occurred.

This offers you some flexibility to make changes, such as displaying friendly error messages, releasing resources, or quickly redirecting the user to a relevant website.

Exception handling assists you in cleaning up the mess without making it appear such. It is the process of tolerating error conditions and responding to them programmatically, resulting in the execution of a supposedly alternate but already planned code sequence.

Read about Exception Handling in PHP 8.

When to Handle an Exception?

Handling exceptions follows a simple rule: handle only those exceptions over which you have control. It’s simple to say, but it’s not always easy to do. We tend to want to save every single possible exception.

We commonly log a message and resume execution since we don’t know what to do with an exception. This frequently leads to the creation of unnecessary code to deal with the failures.

We should only handle exceptions when we have a reasonable chance of correcting the issue and allowing our application to continue running.

When creating code, we must consider three primary kinds of exceptional behavior: possible, probable, and inevitable.

  1. Possible Exceptions
    Theoretically, exceptions are possible, but they are unlikely to occur in the system. When such exceptions occur, it usually means that the system is seriously broken. The issue is irreversible in this scenario, and we should not attempt to handle the exceptions.
  2. Probable Exceptions
    Probable exceptions could occur during the execution of the program, such as a REST call failure caused by a DNS issue. These are the issues we can foresee while developing our program, and in some situations, we can identify a solution. This is where we should concentrate the majority of our exception handling efforts.
  3. Inevitable Exceptions
    Inevitable exceptions will occur frequently. Allowing these exceptions is a typical practice. The proactive detection of the exceptional state and branching around it is a more successful method. Exceptions should not be utilized to control the flow of the program. If we can predict that an operation would result in an exceptional state, we should avoid it wherever possible.

When do Exceptions Occur?

When an exception occurs, take corrective action. Exception handling is about making our programs behave more civilized. Beginning, rescuing, and burying exceptions are anti-patterns that should be avoided.

However, there are situations when the only thing we can do is report an exception. The most basic form of this reporting is to print information to standard error, indicating that an exception has occurred and maybe providing instructions on how to resolve the problem.

More advanced systems use logging and APM technologies like Atatus to report concerns. APM tools streamline the process by centralizing issue reporting and monitoring and promptly identifying ways to improve performance and resolve hidden exceptions.

Atatus support Ruby applications to ensure that no errors are missed while deployments are pushed into production.

Exception handling in Ruby

Exception handling is performed in the majority of languages utilizing «try, throw, and catch» techniques. When the try block is reached, the compiler becomes aware of the possibility of an exception (thrown either by the user or by the language itself) and informs the catch block to handle it.

Read about PHP Exception Handling Using Try Catch.

Ruby chose alternative names for their standard exception handling system. There is a beginning, which is followed by errors that are raised and subsequently rescued. It comes to an end, like all wonderful things, unless you choose to retry.

begin
  # something which might raise an exception
rescue
  # code that deals with some exception
retry
  # retry the code in the begin block
ensure
  # ensure that this code always runs, no matter what
end

In Ruby, exception handling comprises-

  1. Begin — End block
  2. Raise
  3. Rescue
  4. Retry
  5. Ensure

The programming concepts of try, throw, catch, and finally are analogous to the begin-end block, raise, rescue, and ensure.

Be Specific About Your Rescue

What your rescue statement can handle should be specified. Use the most general class name possible if your rescue block can handle many erroneous conditions. This can be StandardError in some circumstances, although it’s most typically a subtree of the class hierarchy behind StandardError.

StandardError and all of its subtypes are captured by a basic rescue, which means it will catch any class raised that extends StandardError. This is troublesome. Only those exceptions should be rescued over which you have control. Other exceptions should be permitted to pass through your rescue statement.

If you use an APM tool like Atatus, you can collect every error that you can’t fix and are just causing unnecessary noise.

Every Ruby error is monitored using error tracking and captured with a full stack trace with the specific line of source code underlined to make error fixing easier and faster. To solve Ruby exceptions and errors, you can collect all the necessary information such as class, message, URL, request agent, version, and so on. You can also discover buggy APIs or third-party services by investigating API failure rates and application crashes.

The begin-rescue

Ruby’s exception handling starts with the begin-rescue block, similar to PHP’s try-catch. In short, the begin-rescue code block is used to handle raised exceptions without interrupting program execution. In other words, you can start executing a block of code while rescuing any raised exceptions.

#1 Rescuing Exceptions

Begin-rescue saves all instances of the StandardError class by default. This includes no method errors, type errors, runtime errors, as well as a custom error that is designed to be rescued within a Ruby application.

Simply enclose the designated section of code in a begin-rescue block to rescue every StandardError:

begin
  # Raise an error here
rescue => e
  # Handle the error here
end

When a StandardError exception is thrown in the begin block, an instance of it is provided to the rescue block as variable e.

#2 Rescuing Specific Exceptions

While rescuing every exception raised is great for simple implementations like generalizing API error responses, the best practice is to rescue for specific exceptions.

To accomplish this, update the above general begin-rescue block to specifically rescue StandardError exceptions:

begin
  # Raise an error here
rescue StandardError => e
  # Handle the error here
end

Although the difference may be minor, if you use a class name after the rescue command, only exceptions of that type will be rescued.

If we wanted to rescue all argument errors, for example, we could build our begin-rescue block as follows:

begin
  # Raise an error here
rescue ArgumentError => e
  # Handle the error here
end

But what if we need to save multiple exception types?

A begin-rescue block can have many rescues, similar to an if-elsif-else chain, which, when combined with a check for the StandardError class, allows you to logically adapt to any issues that may arise:

begin
  # Raise an error here
rescue ArgumentError => e
  # Handle the argument error
rescue TypeError => e
  # Handle the type error
rescue => e
  # Handle the other errors
end

#3 Rescuing All Exceptions

While it may be tempting to rescue every child of the Exception class, due to the structure of the Ruby exception hierarchy, this is typically considered bad practice. This is because, while all Ruby exceptions and errors are extensions of the Exception class, many of them are reserved for Ruby’s internal usage.

SignalException::Interrupt, for example, is used to signal that Ctrl-C has been detected during script execution.

The Interrupt exception would not work as expected if you rescued every child of the Exception class. However, if you wish to rescue every exception raised in Ruby, you may use the same begin-rescue block to specify rescue all Exception exceptions:

begin
  #...
rescue Exception => e
  #...
end

Use the above begin-rescue block sparingly and with caution because it will rescue any exception and error that is raised, from interruptions to syntax errors, and even memory errors.

#4 Rescuing Loops

The «rescue» keyword can also be used on loops. After all, in Ruby, loops are just statements.

The rescue syntax for the various loops is as follows:

while do
    #... loop body
end rescue ...error handler...


begin
  #... loop body
end while rescue ...error handler...


until do
  #... loop body
end rescue ...error handler...


for in expression do
  #... loop body
end rescue ...error handler...

When using a rescue on a loop, there are a few things to keep in mind. The rescue is carried out after the loop has been completed.

#5 Rescue Each

You might be asking whether «rescue» can be used with an «each» iterator. Yes, it is correct.

The following is the syntax:

.each {} rescue ...error handler...

Conclusion

We learned that Ruby has different terminology for handling exceptions. Except for the retry function, the begin/raise/rescue paradigm is similar to the try/throw/catch paradigm used in most other programming languages.

In Ruby, however, throwing and catching allow you to get out of complex constructions quickly. We also learned how to extend the StandardError class to construct application-specific custom Exception classes.

Ruby’s rescue block is quite powerful. It’s far more user-friendly than error codes. Rescue enables you to build more robust solutions by giving you a simple approach to dealing with typical software problems.


Monitor Your Ruby Applications with

Atatus

Atatus keeps track of your Ruby application to give you a complete picture of your clients’ end-user experience. You can determine the source of delayed response times, database queries, and other issues by identifying backend performance bottlenecks for each API request.

To make bug fixing easier, every Ruby error is captured with a full stack trace and the specific line of source code marked. To assist you in resolving the Ruby error, look at the user activities, console logs, and all Ruby requests that occurred at the moment. Error and exception alerts can be sent by email, Slack, PagerDuty, or webhooks.

Try Atatus’s entire features free for 14 days.

TL;DR

Don’t rescue Exception => e (and not re-raise the exception) — or you might drive off a bridge.


Let’s say you are in a car (running Ruby). You recently installed a new steering wheel with the over-the-air upgrade system (which uses eval), but you didn’t know one of the programmers messed up on syntax.

You are on a bridge, and realize you are going a bit towards the railing, so you turn left.

def turn_left
  self.turn left:
end

oops! That’s probably Not Good™, luckily, Ruby raises a SyntaxError.

The car should stop immediately — right?

Nope.

begin
  #...
  eval self.steering_wheel
  #...
rescue Exception => e
  self.beep
  self.log "Caught #{e}.", :warn
  self.log "Logged Error - Continuing Process.", :info
end

beep beep

Warning: Caught SyntaxError Exception.

Info: Logged Error — Continuing Process.

You notice something is wrong, and you slam on the emergency breaks (^C: Interrupt)

beep beep

Warning: Caught Interrupt Exception.

Info: Logged Error — Continuing Process.

Yeah — that didn’t help much. You’re pretty close to the rail, so you put the car in park (killing: SignalException).

beep beep

Warning: Caught SignalException Exception.

Info: Logged Error — Continuing Process.

At the last second, you pull out the keys (kill -9), and the car stops, you slam forward into the steering wheel (the airbag can’t inflate because you didn’t gracefully stop the program — you terminated it), and the computer in the back of your car slams into the seat in front of it. A half-full can of Coke spills over the papers. The groceries in the back are crushed, and most are covered in egg yolk and milk. The car needs serious repair and cleaning. (Data Loss)

Hopefully you have insurance (Backups). Oh yeah — because the airbag didn’t inflate, you’re probably hurt (getting fired, etc).


But wait! There’s more reasons why you might want to use rescue Exception => e!

Let’s say you’re that car, and you want to make sure the airbag inflates if the car is exceeding its safe stopping momentum.

 begin 
    # do driving stuff
 rescue Exception => e
    self.airbags.inflate if self.exceeding_safe_stopping_momentum?
    raise
 end

Here’s the exception to the rule: You can catch Exception only if you re-raise the exception. So, a better rule is to never swallow Exception, and always re-raise the error.

But adding rescue is both easy to forget in a language like Ruby, and putting a rescue statement right before re-raising an issue feels a little non-DRY. And you do not want to forget the raise statement. And if you do, good luck trying to find that error.

Thankfully, Ruby is awesome, you can just use the ensure keyword, which makes sure the code runs. The ensure keyword will run the code no matter what — if an exception is thrown, if one isn’t, the only exception being if the world ends (or other unlikely events).

 begin 
    # do driving stuff
 ensure
    self.airbags.inflate if self.exceeding_safe_stopping_momentum?
 end

Boom! And that code should run anyways. The only reason you should use rescue Exception => e is if you need access to the exception, or if you only want code to run on an exception. And remember to re-raise the error. Every time.

Note: As @Niall pointed out, ensure always runs. This is good because sometimes your program can lie to you and not throw exceptions, even when issues occur. With critical tasks, like inflating airbags, you need to make sure it happens no matter what. Because of this, checking every time the car stops, whether an exception is thrown or not, is a good idea. Even though inflating airbags is a bit of an uncommon task in most programming contexts, this is actually pretty common with most cleanup tasks.

Laura Berge

In Ruby, any time our code throws an error, our program will break. Now, this is often a good feature because errors tell us what our bug is and where in the code it’s located. However, sometimes we have some code that may throw an error, such as getting user input that isn’t what we expected. Let’s say we have a calculator program and a user tries to divide a number by 0.

loop do
    print "Please enter a base number:"
    x = gets
    print "Please enter a divisor:"
    y = gets
    puts x.to_i * y.to_i
end

The code above will throw a ZeroDivisionError if y is equal to zero or a non-number string. (Strings converted to integers in ruby evaluate to 0). However, we don’t want our program to break when the user inputs 0. Instead, we want to let them know that dividing a number by 0 is impossible in math. So, we need to find a way to display a message to the user and keep our program going. We can accomplish this using a rescue in Ruby.

Rescue in Ruby

begin
    # code to execute that may throw an error
rescue
    # code that is executed if the above code throws an error
end

In our case, we could write the code like:

loop do
    print "Please enter a base number:"
    x = gets
    print "Please enter a divisor:"
    y = gets
    puts y
    begin
        puts x.to_i / y.to_i
    rescue
        puts "Sorry, the divisor you entered is invalid. Please enter a non-zero number for the divisor."
    end
end

If the user enters an invalid divisor, the error message is shown and the program continues to run. Now we can even be more specific if we know what errors we’re expecting.

Passing Arguments to Rescue

Rescue by default handles any StandardError, but we can a specific error as an argument. If we pass in an error like TypeError, that rescue block will only execute if the error thrown is a TypeError.

loop do
    print "Please enter a base number:"
    x = gets
    print "Please enter a divisor:"
    y = gets
    puts y
    begin
        puts x.to_i / y.to_i
    rescue ZeroDivisionError
        puts "Sorry, the divisor you entered is invalid. Please enter a non-zero number for the divisor."
    end
end

In this way, we can handle multiple errors differently in the same statement.

begin
    # the code in question
rescue ZeroDivisionError
    # execute code specific to this error
rescue TypeError
    # so on
rescue ArgumentError
    # and so forth
end

We can also operate on the error thrown as a variable.

begin
    # the code in question
rescue => error
    puts "A #{error.class} was thrown: #{error.message}"
end

Else and Ensure

In our begin-end block, we can also use else and ensure statements. The else block will run if no exception is thrown, and the ensure block will always run.

begin
    # code in question
rescue
    puts "Oops we have an error..."
else
    puts "No errors here"
ensure
    puts "I will always run - error or not!"
end

Raising Errors

Now that we know a bit about how to rescue our errors, what if we actually want to raise a custom error of our own? We can do this using raise in Ruby.

raise StandardError.new("custom error message")

If you have a program that needs a new type of error, you can also create one using class inheritance.

class MyCustomError < StandardError
end

if true
    raise MyCustomError("my custom error message")
end

Questions

Feel free to let me know in the comments:

  1. Have you used begin/end blocks in your code before?
  2. Have you ever created a custom error for a program or application?
  3. What is the error message you dread the most?
  4. What is your favorite error message?
  5. Other comments/questions?

Понравилась статья? Поделить с друзьями:
  • Resident evil 4 fatal error steam must be running to play this game
  • Require js catch error
  • Requiem skyrim ошибка
  • Resident evil 4 bio4 exe системная ошибка как исправить
  • Requiem online error 06 failed internet connect