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.