Scala Lesson 37 – Error Handling | Dataplexa

Error Handling in Scala

In this lesson, you will learn how error handling works in Scala. Handling errors correctly is critical for building reliable, maintainable, and production-ready applications.

Scala provides multiple approaches to error handling, ranging from traditional exceptions to more functional and type-safe solutions such as Option, Either, and Try.


Why Error Handling Matters

Errors are unavoidable in real-world applications. They may occur due to:

  • Invalid user input
  • Missing data
  • Network failures
  • File or database issues

Good error handling ensures your program fails gracefully instead of crashing.


Exceptions in Scala

Scala runs on the JVM, so it supports Java-style exceptions using try, catch, and finally.

try {
  val result = 10 / 0
  println(result)
} catch {
  case e: ArithmeticException =>
    println("Cannot divide by zero")
} finally {
  println("Execution finished")
}

While exceptions work, they are not always ideal because they break functional flow and are harder to reason about.


Using Option for Error Handling

Option represents the presence or absence of a value. Instead of throwing an exception, a function can return Some or None.

def findUser(id: Int): Option[String] =
  if (id == 1) Some("Alice") else None

val user = findUser(1)
println(user)

This avoids null values and makes missing data explicit.


Handling Option Safely

Instead of calling get, use safe methods like getOrElse, map, or fold.

val name = findUser(2).getOrElse("Unknown")
println(name)

Using Either for Error Messages

Either is useful when you want to return detailed error information.

  • Right represents success
  • Left represents failure
def divide(a: Int, b: Int): Either[String, Int] =
  if (b == 0) Left("Division by zero")
  else Right(a / b)

println(divide(10, 2))
println(divide(10, 0))

This approach keeps errors explicit and avoids throwing exceptions.


Chaining Either with For-Comprehensions

You can chain multiple computations that may fail using for-comprehensions.

val result = for {
  a <- divide(20, 2)
  b <- divide(a, 2)
} yield b

println(result)

If any step fails, the computation stops and returns the error.


Using Try for Exception-Based Code

Try is useful when working with code that may throw exceptions. It wraps results in Success or Failure.

import scala.util.Try

val result = Try("123".toInt)
println(result)

If an exception occurs, it is captured instead of crashing the program.


Recovering from Failures

Try provides methods like recover and recoverWith.

val safeNumber = Try("abc".toInt).recover {
  case _: NumberFormatException => 0
}

println(safeNumber)

Comparing Error Handling Approaches

  • Exceptions: Simple but less safe
  • Option: Best for missing values
  • Either: Best for detailed errors
  • Try: Best when dealing with legacy or exception-based APIs

Best Practices

  • Avoid throwing exceptions for normal control flow
  • Prefer Option and Either in functional code
  • Use Try when wrapping unsafe operations
  • Always make failure states explicit

📝 Practice Exercises


Exercise 1

Write a function that safely parses a string to an integer using Try.

Exercise 2

Create a function that returns Either for division with error handling.

Exercise 3

Convert a function that throws an exception into one that returns Option.


✅ Practice Answers


Answer 1

def safeParse(s: String): Try[Int] =
  Try(s.toInt)

Answer 2

def safeDivide(a: Int, b: Int): Either[String, Int] =
  if (b == 0) Left("Cannot divide by zero")
  else Right(a / b)

Answer 3

def safeFind(id: Int): Option[String] =
  if (id == 1) Some("User") else None

What’s Next?

In the next lesson, you will learn about Parsers in Scala and how to process structured input data efficiently.