Question
In Ruby, why is it considered bad style to write code like this?
begin
do_something
rescue Exception => e
puts e.message
end
A Ruby quick reference warns: “Don’t rescue Exception. EVER.” Why is rescuing Exception a problem, and what should be used instead for proper error handling?
Short Answer
By the end of this page, you will understand why rescue Exception is usually too broad in Ruby, how Ruby’s exception hierarchy works, and what you should rescue instead in everyday programs. You will also learn safer patterns for error handling, logging, retries, and validation in real Ruby code.
Concept
Ruby exceptions are organized in a class hierarchy. At the top is Exception, and many different error types inherit from it.
When you write:
rescue Exception => e
you are catching almost everything, including exceptions that usually should not be intercepted by normal application code.
Why this is a problem
Exception includes not only typical program errors, but also system-level and control-flow-related exceptions such as:
SystemExit— raised when the program is trying to exitInterrupt— raised when the user presses Ctrl+CNoMemoryError— raised when memory runs outSignalException— related to operating system signals
If you rescue Exception, your program may:
- ignore shutdown requests
- refuse to stop on Ctrl+C
- hide serious system failures
- continue running in a broken or unsafe state
That makes debugging harder and can create surprising behavior.
What Ruby rescues by default
A plain rescue without specifying a class rescues StandardError, not Exception:
begin
do_something
rescue => e
puts e.message
end
This is the normal behavior Ruby expects for application-level error handling.
Why StandardError is usually correct
Most everyday errors inherit from StandardError, including:
RuntimeErrorNoMethodErrorArgumentErrorNameErrorIOError- many library-specific errors
These are the kinds of exceptions your app can often log, handle, retry, or turn into user-friendly messages.
General rule
- Rescue the most specific exception class you expect.
- If you need a broad catch-all for application errors, rescue
StandardError. - Avoid rescuing
Exceptionunless you have a very unusual reason and fully understand the consequences.
Mental Model
Think of exceptions like alarms in a building.
StandardErroralarms are room-level problems: a broken light, a locked door, a small leak.Exceptionincludes those alarms plus the building fire alarm, evacuation signal, and power failure warnings.
If you silence all alarms, you might also ignore the ones telling everyone to leave the building.
That is what rescue Exception does: it catches not only normal program problems, but also signals that the process should stop or cannot safely continue.
Syntax and Examples
Preferred syntax
In Ruby, these are the common forms:
begin
risky_operation
rescue StandardError => e
puts "Handled: #{e.message}"
end
Or simply:
begin
risky_operation
rescue => e
puts "Handled: #{e.message}"
end
A bare rescue is shorthand for rescuing StandardError.
Better: rescue specific exceptions
begin
number = Integer(user_input)
rescue ArgumentError
puts "Please enter a valid integer"
end
This is better because it only handles the error you actually expect.
Example: too broad
begin
exit
rescue Exception => e
puts "Caught: #{e.class}"
end
This can catch SystemExit, which prevents the program from exiting normally.
Example: safer version
begin
value = Integer("abc")
rescue ArgumentError => e
puts "Conversion failed: #{e.message}"
end
Why this is good
- It documents what can go wrong.
- It avoids swallowing unrelated exceptions.
- It makes debugging easier.
- It keeps system-level exceptions untouched.
Step by Step Execution
Consider this example:
begin
puts "Start"
number = Integer("abc")
puts "Number: #{number}"
rescue ArgumentError => e
puts "Handled error: #{e.message}"
end
puts "Program continues"
Step-by-step
- Ruby enters the
beginblock. puts "Start"runs.- Ruby executes
Integer("abc"). - Ruby cannot convert
"abc"to an integer. - It raises an
ArgumentError. - Ruby looks for a matching
rescueclause. rescue ArgumentError => ematches the raised exception.- The rescue block runs and prints the error message.
- Execution continues after the
end. puts "Program continues"runs.
Output
Start
Handled error: invalid value for Integer(): "abc"
Program continues
What if we had used rescue Exception?
It would also catch this error, but it would catch far more than needed. That makes the code less precise and more dangerous.
Real World Use Cases
1. Validating user input
begin
age = Integer(params[:age])
rescue ArgumentError, TypeError
age = nil
end
Used when form input may be invalid.
2. Reading files safely
begin
content = File.read("config.txt")
rescue Errno::ENOENT
content = "default config"
end
Used when a file might not exist.
3. Network and API calls
begin
response = api_client.fetch_user(id)
rescue Timeout::Error
puts "Request timed out"
end
Used for temporary failures that can be retried or reported.
4. Background jobs
begin
process_job(job)
rescue StandardError => e
logger.error(e.message)
logger.error(e.backtrace.join("\n"))
end
A job runner may rescue StandardError to log failures and mark a job as failed, but should still avoid rescuing Exception.
5. Command-line tools
CLI tools often allow Ctrl+C to stop execution. Rescuing Exception may block that behavior by catching Interrupt.
Real Codebase Usage
In real projects, developers rarely want a giant catch-all. They usually use one of these patterns.
Guard clauses before exceptions happen
Instead of rescuing a broad error, check inputs early:
def divide(a, b)
return nil if b == 0
a / b
end
Rescue only expected failures
def parse_port(value)
Integer(value)
rescue ArgumentError, TypeError
3000
end
Log and re-raise when needed
def save_record(record)
record.save!
rescue ActiveRecord::RecordInvalid => e
logger.warn("Validation failed: #{e.message}")
raise
end
This pattern keeps visibility while preserving the original failure.
Wrap low-level errors in domain-specific errors
class PaymentError < StandardError; end
def charge_customer(client, amount)
client.charge(amount)
rescue Timeout::Error, SocketError => e
raise PaymentError, "Payment service unavailable: #{e.message}"
end
This gives higher-level code a cleaner error to handle.
Top-level app boundaries
Sometimes an application entry point rescues StandardError to log unhandled errors:
begin
run_app
rescue StandardError => e
logger.fatal(e.message)
logger.fatal(e.backtrace.join("\n"))
exit(1)
end
Even here, Exception is usually still too broad.
Common Mistakes
Mistake 1: Rescuing everything
begin
do_work
rescue Exception => e
puts "Something went wrong"
end
Why it is bad
- hides important failures
- catches Ctrl+C interrupts
- may prevent clean shutdown
Better
begin
do_work
rescue StandardError => e
puts "Something went wrong: #{e.message}"
end
Or rescue a specific error class.
Mistake 2: Swallowing errors silently
begin
do_work
rescue => e
end
Why it is bad
The program fails, but you get no clue why.
Better
begin
do_work
rescue => e
warn e.message
raise
end
If you handle an error, do it intentionally.
Mistake 3: Using exceptions for normal control flow
def admin_name(user)
begin
user.admin.name
rescue
"Unknown"
end
end
Why it is bad
This can hide unrelated bugs like misspelled method names.
Better
def admin_name(user)
return "Unknown" unless user&.admin
user.admin.name
end
Mistake 4: Rescuing the wrong class
begin
File.read("missing.txt")
rescue StandardError
puts "Bad input"
end
Problem
This works, but it is too general.
Better
begin
File.read("missing.txt")
rescue Errno::ENOENT
puts "File not found"
end
Comparisons
| Approach | What it catches | Typical use | Risk level |
|---|---|---|---|
rescue Exception | Almost all exceptions, including system-level ones | Very rare, special infrastructure code | High |
rescue StandardError | Most normal application errors | General app-level error handling | Medium |
rescue SomeSpecificError | Only the named error type | Best for expected failures | Low |
bare rescue | Same as rescue StandardError | Short form in simple cases | Medium |
Exception vs StandardError
Exceptionis the root of Ruby’s exception hierarchy.StandardErroris the normal parent class for application errors.- Most code should rescue
StandardErroror a subclass.
Specific rescue vs broad rescue
| Style | Example | Benefit | Drawback |
|---|---|---|---|
| Specific | rescue ArgumentError | Precise and clear | May need multiple rescue clauses |
| Broad app-level | rescue StandardError | Convenient for logging or fallback | Can still hide unrelated app bugs |
| Too broad | rescue Exception | Catches nearly everything | Dangerous and misleading |
Cheat Sheet
Quick rules
- Do not rescue
Exceptionin normal Ruby code. - Use
rescue => eorrescue StandardError => efor general application errors. - Prefer rescuing the specific exception class you expect.
- Log errors when helpful.
- Re-raise errors if you cannot truly handle them.
Common syntax
begin
risky_code
rescue => e
puts e.message
end
begin
risky_code
rescue ArgumentError => e
puts e.message
end
begin
risky_code
rescue ArgumentError, TypeError => e
puts e.message
end
Good defaults
- Expected parse failure:
ArgumentError - Missing file:
Errno::ENOENT - Validation failure: library-specific error
- Generic app boundary:
StandardError
Avoid
rescue Exception => e
unless you are writing very low-level infrastructure and intentionally handling system-level exceptions.
Important idea
A bare rescue in Ruby means:
rescue StandardError
FAQ
Why is rescue Exception bad in Ruby?
Because it catches far more than normal application errors, including SystemExit and Interrupt, which usually should not be blocked.
What should I rescue instead in Ruby?
Usually rescue a specific exception class. If you need a broad catch for app errors, rescue StandardError.
Does bare rescue catch Exception?
No. A bare rescue catches StandardError by default.
Can rescue Exception stop Ctrl+C from working?
Yes. Ctrl+C raises Interrupt, which is under Exception. Rescuing Exception can prevent the program from stopping normally.
Is it ever okay to rescue Exception?
Rarely. It may be used in very low-level framework or process-management code, but it is not appropriate for ordinary application logic.
What is the difference between Exception and StandardError?
Exception is the top-level parent of Ruby exceptions. StandardError is the common parent for normal application errors.
Should I always rescue a specific error type?
Yes, when possible. It makes the code clearer and avoids accidentally hiding unrelated problems.
Mini Project
Description
Build a small Ruby script that reads a filename from the user, tries to open the file, and converts each line to an integer. This demonstrates how to rescue specific exceptions instead of using Exception, and how to keep different error cases separate and understandable.
Goal
Create a script that safely handles missing files and invalid number input using specific Ruby exception classes.
Requirements
- Ask the user for a filename.
- Read all lines from the file.
- Convert each line to an integer.
- If the file does not exist, print a helpful message.
- If a line is not a valid integer, print which line failed.
- Do not use
rescue Exception.
Keep learning
Related questions
Add a Default Value to an Existing Column in a Rails Migration
Learn how to add or change a default value on an existing database column using Rails Active Record migrations safely.
Calling a Class Method from an Instance in Ruby
Learn how to call a class method from an instance in Ruby using self.class, with examples, pitfalls, and practical usage patterns.
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.