Exceptions and Error Handling
Prerequisites: Classes and objects, Working with nullable values
When something goes wrong while a program runs — dividing by zero,
converting "abc" to a number, a missing file — Kotlin throws an
exception. If nothing catches it, the program stops.
Real-world analogy: an exception is a fire alarm. catch is the
plan for what to do when it goes off.
try / catch / finally
fun main() {
val inputs = listOf("42", "abc")
for (input in inputs) {
try {
val n = input.toInt()
println("Parsed $n")
} catch (e: NumberFormatException) {
println("Not a number: $input")
} finally {
println("Done with '$input'")
}
}
}
Parsed 42
Done with '42'
Not a number: abc
Done with 'abc'
catch (e: Type)handles a specific kind of exception.finallyruns no matter what — for cleanup.
try is an expression
fun main() {
val input = "abc"
val number = try { input.toInt() } catch (e: NumberFormatException) { 0 }
println(number)
}
0
Throwing, require, and check
fun withdraw(balance: Double, amount: Double): Double {
require(amount > 0) { "Amount must be positive" }
if (amount > balance) {
throw InsufficientFundsException(amount - balance)
}
return balance - amount
}
class InsufficientFundsException(val shortfall: Double) :
Exception("Insufficient funds: short by $shortfall")
fun main() {
try {
withdraw(50.0, 80.0)
} catch (e: InsufficientFundsException) {
println(e.message)
println("Please deposit ${e.shortfall}")
}
try {
withdraw(50.0, -5.0)
} catch (e: IllegalArgumentException) {
println(e.message)
}
}
Insufficient funds: short by 30.0
Please deposit 30.0
Amount must be positive
require(...)checks arguments (throwsIllegalArgumentException).check(...)checks the object's state (throwsIllegalStateException).- Custom exceptions subclass
Exceptionand can carry extra data.
runCatching and Result
runCatching turns an exception into a Result value:
fun main() {
val result = runCatching { "12x".toInt() }
println(result.isFailure)
println(result.getOrDefault(-1))
println(runCatching { "12".toInt() }.getOrNull())
}
true
-1
12
All exceptions are unchecked
Unlike Java, Kotlin doesn't force you to declare or catch exceptions.
That keeps code shorter but means you decide where failures are
handled. For expected, everyday failures ("user not found"), returning
null or a sealed result type is often clearer than throwing.
Common mistakes
- Catching
Exception(orThrowable) everywhere and hiding real bugs. Catch the specific types you can handle. - Using
runCatchingaround code that runs coroutines — it also catches the cancellation exceptions coroutines rely on (see Level 3).
Practice
Write fun parseAge(text: String): Int that throws
IllegalArgumentException with a helpful message if the text isn't a
number or isn't between 0 and 150. Call it with "200" in a
try/catch and print the message.
Show solution
fun parseAge(text: String): Int {
val age = requireNotNull(text.toIntOrNull()) { "'$text' is not a number" }
require(age in 0..150) { "Age $age is out of range (0-150)" }
return age
}
fun main() {
try {
parseAge("200")
} catch (e: IllegalArgumentException) {
println("Error: ${e.message}")
}
}
Error: Age 200 is out of range (0-150)