Practical examples of Ruby exception handling examples for real apps
Simple examples of Ruby exception handling examples with begin / rescue
Before getting fancy, it helps to see a small, honest example of Ruby exception handling that you might actually type into irb.
begin
puts "Enter a number:"
input = gets.chomp
number = Integer(input) # may raise ArgumentError
puts "You entered: #{number}"
rescue ArgumentError => e
warn "Invalid number: #{e.message}"
end
This is one of the best examples for beginners because it shows three important habits:
- Rescue a specific exception (
ArgumentError), not everything. - Use the exception object (
e) for a meaningful message. - Let other exceptions bubble up so you still see real bugs.
If you want a softer UX, you can loop until the user gives valid input, but the core pattern stays the same.
File I/O: example of handling missing or unreadable files
File handling is where you see some of the clearest examples of Ruby exception handling examples in everyday scripts. Files disappear, permissions change, encodings get messy.
file_path = "config/settings.yml"
begin
content = File.read(file_path)
puts "Loaded settings (#{content.bytesize} bytes)"
rescue Errno::ENOENT => e
warn "Config file not found: #{file_path}"
rescue Errno::EACCES => e
warn "Permission denied reading: #{file_path}"
rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError => e
warn "Bad encoding in file #{file_path}: #{e.class} - #{e.message}"
end
Real examples like this highlight why you rarely want to rescue StandardError in one big net. Different file exceptions often deserve different responses: maybe you create a default config if the file is missing, but you definitely want to alert someone if permissions are broken.
Network and API calls: best examples from 2024–2025 Ruby apps
Modern Ruby apps spend a lot of time talking to APIs. Network calls are some of the most useful examples of Ruby exception handling examples, because they show how to mix retries, logging, and fallbacks.
Here’s a pattern using Net::HTTP and a basic retry loop:
require "net/http"
require "uri"
uri = URI("https://api.example.com/data")
max_retries = 3
attempt = 0
begin
attempt += 1
response = Net::HTTP.get_response(uri)
case response
when Net::HTTPSuccess
puts "Got data: #{response.body[0..50]}..."
when Net::HTTPTooManyRequests
warn "Rate limited; status: #{response.code}"
else
warn "Unexpected response: #{response.code} #{response.message}"
end
rescue SocketError, Errno::ECONNREFUSED, Net::OpenTimeout, Net::ReadTimeout => e
warn "Network error (attempt #{attempt}): #{e.class} - #{e.message}"
retry if attempt < max_retries
warn "Giving up after #{attempt} attempts"
end
In 2024–2025, this sort of code usually sits behind a higher‑level HTTP client (Faraday, HTTParty, or a dedicated SDK). The pattern stays the same, though: rescue concrete network exceptions, log them, and decide when to retry versus fail fast. These are the real examples that separate a hobby script from a production‑ready service.
Scoped rescue as a modifier: tiny examples include one‑liners
Ruby lets you attach rescue directly to a single expression. It’s handy, but often abused. Here’s a small example of Ruby exception handling using the modifier form without hiding real bugs:
## Bad: hides every kind of StandardError
user = User.find_by(id: params[:id]) rescue nil
## Better: rescue just what you expect
user = begin
User.find(params[:id])
rescue ActiveRecord::RecordNotFound
nil
end
Or, for a very narrow case where you only care about one known failure:
value = Integer(params[:limit]) rescue 10 # only if you're sure
These examples of Ruby exception handling examples show why clarity beats cleverness. If someone new to the codebase can’t tell what you’re rescuing or why, the one‑liner probably needs to be expanded.
Examples of Ruby exception handling examples in Rails controllers
Rails controllers are a gold mine for practical patterns. One common example of controller‑level error handling is mapping exceptions to HTTP responses.
class ArticlesController < ApplicationController
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
rescue_from ActionController::ParameterMissing, with: :render_bad_request
def show
article = Article.find(params[:id])
render json: article
end
def create
attrs = params.require(:article).permit(:title, :body)
article = Article.create!(attrs)
render json: article, status: :created
end
private
def render_not_found(error)
render json: { error: error.message }, status: :not_found
end
def render_bad_request(error)
render json: { error: error.message }, status: :bad_request
end
end
This is one of the best examples of Ruby exception handling examples in a web context:
rescue_fromcentralizes behavior instead of repeatingbegin/rescueblocks.- Different exceptions map cleanly to different HTTP status codes.
- The controller stays focused on happy‑path logic.
In 2024–2025, you’ll also see teams pair patterns like this with error tracking tools such as Sentry or Honeybadger, so unhandled exceptions still get captured and triaged.
Background jobs: retrying with Sidekiq and other workers
Background job systems are another area where examples include nuanced exception handling. For Sidekiq, the default behavior already retries jobs on most errors, but you can customize it.
class SyncUserJob
include Sidekiq::Job
sidekiq_options retry: 5
def perform(user_id)
user = User.find(user_id)
ExternalCrm.sync!(user) # may raise network or API errors
rescue ActiveRecord::RecordNotFound => e
# # No point retrying if the user is gone
Sidekiq.logger.warn("User not found: #{user_id} - #{e.message}")
end
end
This example of a background job focuses on a simple rule: retry transient failures, don’t retry permanent ones. By rescuing ActiveRecord::RecordNotFound inside the job, you prevent Sidekiq from hammering an already‑deleted record.
For more advanced setups, teams often combine this with circuit‑breaker patterns or rate‑limit checks, especially when talking to third‑party APIs that enforce strict quotas.
Using ensure and else: real examples you should actually use
Many developers ignore else and misuse ensure. Here’s a practical pattern that makes both work for you.
log = Logger.new($stdout)
begin
log.info "Opening connection..."
conn = Database.connect
result = conn.query("SELECT * FROM users")
# # `else` only runs if no exception was raised
# # so it's a nice place for success‑only logic
puts "Fetched #{result.count} users"
rescue Database::ConnectionError => e
log.error "DB connection failed: #{e.message}"
rescue Database::QueryError => e
log.error "Query failed: #{e.message}"
else
log.info "Query completed successfully"
ensure
# # `ensure` runs no matter what
conn&.close
log.info "Connection closed"
end
These examples of Ruby exception handling examples highlight a good rule of thumb:
- Use
elsefor code that should run only when everything succeeds. - Use
ensurefor cleanup that must run even when things go wrong.
This pattern mirrors resource‑safety ideas you’ll see in other languages, and it lines up with general reliability advice from software engineering programs at universities like Harvard, which emphasize cleaning up resources predictably.
Defining custom exceptions: examples include domain‑specific errors
At some point, StandardError subclasses from the standard library won’t be enough. Real examples in production apps usually define their own exception hierarchy.
module Payments
class Error < StandardError; end
class CardDeclined < Error; end
class FraudSuspected < Error; end
class GatewayUnavailable < Error; end
end
class PaymentProcessor
def charge!(user, amount_cents)
response = gateway.charge(user.card_token, amount_cents)
case response.code
when "card_declined"
raise Payments::CardDeclined, response.message
when "fraud_suspected"
raise Payments::FraudSuspected, response.message
when "gateway_unavailable"
raise Payments::GatewayUnavailable, "Payment gateway offline"
end
response
end
private
def gateway
@gateway ||= ThirdPartyGateway.new
end
end
Then, in a controller or service object, you can handle these in a focused way:
begin
PaymentProcessor.new.charge!(current_user, 5000)
rescue Payments::CardDeclined => e
flash[:alert] = "Your card was declined: #{e.message}"
rescue Payments::FraudSuspected => e
flash[:alert] = "We detected a problem with this payment. Please contact support."
rescue Payments::GatewayUnavailable => e
flash[:alert] = "Payments are temporarily unavailable. Please try again later."
end
This is one of the best examples of Ruby exception handling examples for domain logic: the exceptions express business meaning, not just low‑level technical failure.
Observability and logging: 2024–2025 trends around exceptions
In 2024 and 2025, exception handling in Ruby isn’t just about rescue blocks; it’s about observability. Teams want to know when exceptions happen, where, and how often.
Some patterns you’ll see:
- Structured logging (JSON logs with fields like
error_class,error_message,user_id). - Correlating exceptions with trace IDs in systems like OpenTelemetry.
- Using error budgets and SLOs, often inspired by reliability guidance from organizations like NIST and major engineering programs.
Here’s a small example of Ruby exception handling examples wired into structured logging:
require "logger"
require "json"
class JsonLogger
def initialize(io = $stdout)
@logger = Logger.new(io)
end
def error(message, **context)
payload = { level: "ERROR", message:, **context }
@logger.error(payload.to_json)
end
end
logger = JsonLogger.new
begin
risky_operation
rescue => e
logger.error(
"Operation failed",
error_class: e.class.name,
error_message: e.message,
backtrace: e.backtrace&.first(5)
)
raise # re‑raise so upstream still sees the failure
end
In other words, modern examples include not just catching the error, but also making sure future you can see what happened without SSHing into a server and tailing plain‑text logs.
Common mistakes in Ruby exception handling (and better examples)
You can learn a lot from bad patterns. Here are a few anti‑patterns, followed by better examples of Ruby exception handling examples.
Catching everything and doing nothing
begin
do_something_risky
rescue
# # ignore
end
Better:
begin
do_something_risky
rescue SpecificError => e
warn "Handled specific failure: #{e.message}"
end
Rescuing too broadly in libraries
Library code that rescues StandardError and then returns nil is a debugging nightmare. Instead, libraries should raise meaningful exceptions and let the application decide how to handle them.
Swallowing interrupts
begin
long_running_task
rescue Exception
# # this will catch Interrupt and SystemExit too — bad idea
end
Better:
begin
long_running_task
rescue StandardError => e
# # handle normal errors
warn "Task failed: #{e.message}"
end
Or, if you truly need to intercept interrupts, re‑raise them after cleanup.
These real examples help keep exception handling honest and prevent the “it just silently stopped working” bug class that everyone hates.
FAQ: short answers with real examples
Q: Can you show a simple example of Ruby exception handling in a loop?
Yes. Here’s a small pattern for retrying user input:
3.times do
print "Enter an integer: "
begin
value = Integer(gets.chomp)
puts "Thanks, you entered #{value}"
break
rescue ArgumentError
puts "That wasn't an integer. Try again."
end
end
Q: When should I define custom exception classes?
Define them when you need to express domain‑specific failures. For instance, Payments::CardDeclined or Inventory::OutOfStock give you clear, focused examples of Ruby exception handling examples that map directly to business rules.
Q: Is rescue nil ever okay?
It’s acceptable only when you are absolutely sure about the exception type and are intentionally ignoring it, such as optional logging or non‑critical metrics. In most cases, a clearer example of Ruby exception handling is to rescue a specific error and document why you’re ignoring it.
Q: How do I test exception handling in Ruby?
In RSpec, you can use expect { ... }.to raise_error(SomeError) for raising, and you can also assert on side effects (like logging or return values) when you rescue. Writing tests around these examples of Ruby exception handling examples keeps your error paths from rotting over time.
The bottom line: the best examples of Ruby exception handling examples are specific, honest about failure, and wired into logging and monitoring. If your rescue blocks make it easier to debug problems six months from now, you’re doing it right.
Related Topics
Practical examples of examples of Ruby regular expressions examples
The best examples of Ruby looping constructs: 3 practical examples for real projects
Practical examples of Ruby command-line interface examples for 2025
The best examples of examples of basic Ruby syntax examples for beginners
Modern examples of Ruby web scraping examples - practical code snippets
Practical examples of Ruby exception handling examples for real apps
Explore More Ruby Code Snippets
Discover more examples and insights in this category.
View All Ruby Code Snippets