Custom Exception Classes
A custom exception class is your own error type — usually a subclass of StandardError — that gives failures a meaningful name and can carry extra data for the code that rescues it.
Learn Custom Exception Classes 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.
You'll define error classes, raise and rescue them by type, attach attributes, and use retry and ensure .
What You'll Learn in This Lesson
1️⃣ Defining and Raising a Custom Error
Create an error type by subclassing StandardError — often that's the entire class body. Then raise YourError, "message" to signal it, and rescue YourError => e to catch exactly that type while letting unrelated errors pass through.
2️⃣ Attributes, Messages & Rescuing by Type
Override initialize to store extra data and call super(message) so the message still works. Expose data with attr_reader , then read it in the handler. List multiple rescue clauses — most specific first — to handle each error type differently.
3️⃣ Recovering with retry and ensure
retry inside a rescue re-runs the begin block — ideal for transient failures, as long as you cap the attempts. ensure runs whether or not an exception happened, making it the right home for cleanup.
Your turn. Finish a tiny bank withdrawal that raises a custom error when funds are short. Fill in each ___ .
Build an InvalidEmailError that stores the offending address, then process a list so one bad email doesn't halt the rest. This per-item rescue pattern is everywhere in real systems. Run with ruby register.rb .
📋 Quick Reference — Exceptions
Practice quiz
Which class should a custom exception usually inherit from?
- Exception
- RuntimeError only
- StandardError
- Object
Answer: StandardError. Inherit from StandardError so a bare rescue catches it, without swallowing system-level signals.
Why NOT inherit directly from Exception?
- A bare rescue won't catch it, and you risk swallowing signals like Ctrl-C
- It is slower
- It can't store a message
- It is deprecated
Answer: A bare rescue won't catch it, and you risk swallowing signals like Ctrl-C. A bare rescue catches StandardError, not all of Exception. Inheriting from Exception means rescue misses it and may swallow SignalInterrupt or SystemExit.
How do you attach extra data, like a status code, to a custom exception?
- You cannot
- Use a global variable
- Pass it to rescue
- Define initialize, store it in an ivar, expose with attr_reader, and call super(message)
Answer: Define initialize, store it in an ivar, expose with attr_reader, and call super(message). Override initialize to store the data, expose it with attr_reader, and call super(message) so the message machinery still works.
What does do inside a rescue clause?
- Re-raises the error
- Jumps back to the start of the begin block
- Skips to ensure
- Exits the program
Answer: Jumps back to the start of the begin block. retry restarts the begin block — useful for transient failures, as long as you cap attempts.
When does the clause run?
- No matter what — success or failure
- Only on success
- Only on error
- Only when retry is used
Answer: No matter what — success or failure. ensure always runs, making it the right home for cleanup like closing files.
When listing multiple rescue clauses, which order is correct?
- Most general first
- Alphabetical
- Most specific first, then general
- Order doesn't matter
Answer: Most specific first, then general. Ruby checks rescues top to bottom; a broad StandardError first would shadow specific ones, so put specific first.
In a custom initialize, what happens if you forget to call ?
- The class won't load
- The standard message machinery is lost
- retry stops working
- ensure runs twice
Answer: The standard message machinery is lost. Without super(message), the exception's message handling is lost.
What does do?
- Defines the error class
- Rescues the error
- Returns nil
- Signals the error with that message
Answer: Signals the error with that message. raise signals the exception and creates the object with the given message.
To catch a whole family of your app's errors with one rescue, you should...
- Rescue Exception
- Create a base AppError < StandardError and subclass it
- Use retry
- Rescue NilClass
Answer: Create a base AppError < StandardError and subclass it. A base AppError < StandardError lets one catch every subclass in the family.
What does an uncapped risk?
- A syntax error
- Swallowing the message
- Looping forever on a persistent error
- Running ensure twice
Answer: Looping forever on a persistent error. Without an attempt counter, retry can loop forever if the error keeps recurring.