← Back to Kotlin Course | Chapter 13: Error Handling & Testing | Lesson 2 of 6

Custom Exceptions

A custom exception is your own named kind of error, so your program can describe exactly what went wrong in your own words.

Declaring a Custom Exception

A custom exception class extends Exception (or a subclass of it), typically forwarding a message to the superclass constructor so it can be printed and inspected like any built-in exception.

Example: Declaring a Custom Exception

markup
class InsufficientFundsException(message: String) : Exception(message)

fun withdraw(balance: Double, amount: Double): Double {
    if (amount > balance) throw InsufficientFundsException("Cannot withdraw $amount from balance $balance")
    return balance - amount
}

fun main() {
    try {
        withdraw(100.0, 150.0)
    } catch (e: InsufficientFundsException) {
        println("Error: ${e.message}")
    }
}

Adding Extra Properties

A custom exception can carry additional properties beyond a message, giving catching code structured information to act on programmatically instead of just parsing text.

Example: Adding Extra Properties

markup
class ValidationException(val field: String, message: String) : Exception(message)

fun validateAge(age: Int) {
    if (age < 0) throw ValidationException("age", "Age cannot be negative")
}

fun main() {
    try {
        validateAge(-5)
    } catch (e: ValidationException) {
        println("Invalid field ${e.field}: ${e.message}")
    }
}

Building a Hierarchy of Custom Exceptions

Related custom exceptions can share a common open base class, letting calling code catch either a specific exception type or the more general base type depending on how precise it needs to be.

Example: Building a Hierarchy of Custom Exceptions

markup
open class AppException(message: String) : Exception(message)
class NetworkException(message: String) : AppException(message)
class DatabaseException(message: String) : AppException(message)

fun main() {
    val errors = listOf(NetworkException("Timeout"), DatabaseException("Connection lost"))
    for (error in errors) {
        try {
            throw error
        } catch (e: AppException) {
            println("App error: ${e.message}")
        }
    }
}

Including a Cause

A custom exception's constructor can also accept and forward a cause (another Throwable), preserving the original underlying error while presenting a clearer, higher-level exception to callers.

Example: Including a Cause

markup
class DataLoadException(message: String, cause: Throwable) : Exception(message, cause)

fun main() {
    try {
        try {
            throw NumberFormatException("bad number")
        } catch (e: NumberFormatException) {
            throw DataLoadException("Failed to load data", e)
        }
    } catch (e: DataLoadException) {
        println("${e.message}, caused by: ${e.cause?.message}")
    }
}
Common Mistakes
  1. Extending Throwable directly instead of Exception (or RuntimeException), which is almost never what you actually want for application-level errors.
  2. Forgetting to pass a message (and optionally a cause) up to the superclass constructor, resulting in exceptions with no useful description when printed.
  3. Creating overly specific exception subclasses for every tiny situation instead of using a smaller number of well-designed, meaningful exception types.
Chapter Summary
  • A custom exception is typically declared as class MyException(message: String) : Exception(message).
  • Extending Exception (a checked-style base, though Kotlin doesn't enforce checked exceptions) or RuntimeException are both common choices.
  • Custom exceptions can carry extra properties beyond just a message, useful for programmatic error handling.
  • A well-designed set of custom exceptions communicates failure reasons clearly to code that catches them.
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.