Exception Handling
Ruby is a dynamic, beginner-friendly programming language, and exception handling lets your program respond to errors gracefully — catching problems with begin/rescue instead of crashing.
Learn Exception Handling in our free Ruby course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Ruby course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
By the end of this lesson you'll rescue specific errors, guarantee cleanup with ensure, raise and define your own exceptions, and retry failed operations.
What You'll Learn in This Lesson
1️⃣ begin, rescue, and the Error Object
Put risky code in a begin block; if it raises, the matching rescue runs instead of crashing. Capture the error with => e to read its message . Always rescue the most specific class you can.
2️⃣ ensure, raise, Custom Errors, and retry
ensure always runs — ideal for cleanup. raise throws an error, and you can define your own error classes by subclassing StandardError for clarity. retry re-runs the block — guard it with a counter so it can't loop forever.
Your turn. Make a safe_divide method that never crashes and always logs an attempt. Complete the TODO rescue, then run it.
Parse and validate input, raising and rescuing an ArgumentError for bad values. Run with ruby age.rb .
📋 Quick Reference — Exceptions
Practice quiz
Which error does raise in Ruby?
- ArgumentError
- TypeError
- ZeroDivisionError
- RuntimeError
Answer: ZeroDivisionError. Dividing an integer by zero raises ZeroDivisionError with the message 'divided by 0'.
What block catches a raised error so the program keeps running?
- begin/rescue
- begin/ensure
- try/catch
- if/else
Answer: begin/rescue. begin/rescue wraps risky code; the rescue runs instead of crashing.
How do you capture the error object to read its message?
- rescue e:
- catch e
- rescue.message
- rescue => e, then e.message
Answer: rescue => e, then e.message. Use , then read e.message.
When does the clause run?
- Only on success
- Whether or not an error happened
- Only on failure
- Only after retry
Answer: Whether or not an error happened. ensure always runs — success, rescued error, or unrescued error — so it's ideal for cleanup.
Why avoid ?
- It is too broad and swallows signals and system errors
- It is a syntax error
- It can't read messages
- It is slower
Answer: It is too broad and swallows signals and system errors. rescue Exception catches everything including Ctrl-C and out-of-memory, which you should not swallow. Rescue StandardError or a specific class.
How do you define your own exception type?
- def WithdrawalError
- error WithdrawalError
- class WithdrawalError < StandardError; end
- raise class WithdrawalError
Answer: class WithdrawalError < StandardError; end. Subclass StandardError: .
What does do, and how should you guard it?
- Re-raises; guard with ensure
- Re-runs the begin block; cap attempts with a counter
- Exits; no guard needed
- Skips the rescue; use a flag
Answer: Re-runs the begin block; cap attempts with a counter. retry re-runs the begin block; without an attempt counter it can loop forever.
What does raise?
- ZeroDivisionError
- It returns nil
- TypeError
- ArgumentError
Answer: ArgumentError. Integer() raises ArgumentError when the string isn't a valid number.
Where can a clause live without an explicit ?
- Nowhere
- Directly inside a method (def) body
- Only at the top level
- Inside a class but not a method
Answer: Directly inside a method (def) body. A method body acts as the begin block, so you can write rescue directly inside a def.
What does an empty with no logging risk?
- A crash
- Running ensure twice
- Silently hiding bugs
- An infinite loop
Answer: Silently hiding bugs. Swallowing errors silently hides bugs; at least log e.message.