This section looks at some of the common ways to detect and respond to failure.
You can surface failures with
ZIO#either, which takes an
ZIO[R, E, A] and produces an
ZIO[R, Nothing, Either[E, A]].
val zeither: UIO[Either[String, Int]] =
You can submerge failures with
ZIO.absolve, which is the opposite of
either and turns an
ZIO[R, Nothing, Either[E, A]] into a
ZIO[R, E, A]:
def sqrt(io: UIO[Double]): IO[String, Double] =
if (value < 0.0) Left("Value must be >= 0.0")
Catching All Errors
If you want to catch and recover from all types of errors and effectfully attempt recovery, you can use the
val z: IO[IOException, Array[Byte]] =
In the callback passed to
catchAll, you may return an effect with a different error type (or perhaps
Nothing), which will be reflected in the type of effect returned by
Catching Some Errors
If you want to catch and recover from only some types of exceptions and effectfully attempt recovery, you can use the
val data: IO[IOException, Array[Byte]] =
case _ : FileNotFoundException =>
catchSome cannot reduce or eliminate the error type, although it can widen the error type to a broader class of errors.
You can try one effect, or, if it fails, try another effect, with the
val primaryOrBackupData: IO[IOException, Array[Byte]] =
Either data types have
fold, which let you handle both failure and success at the same time. In a similar fashion,
ZIO effects also have several methods that allow you to handle both failure and success.
The first fold method,
fold, lets you non-effectfully handle both failure and success, by supplying a non-effectful handler for each case:
lazy val DefaultData: Array[Byte] = Array(0, 0)
val primaryOrDefaultData: UIO[Array[Byte]] =
_ => DefaultData,
data => data)
The second fold method,
foldM, lets you effectfully handle both failure and success, by supplying an effectful (but still pure) handler for each case:
val primaryOrSecondaryData: IO[IOException, Array[Byte]] =
_ => openFile("secondary.data"),
data => ZIO.succeed(data))
Nearly all error handling methods are defined in terms of
foldM, because it is both powerful and fast.
In the following example,
foldM is used to handle both failure and success of the
val urls: UIO[Content] =
error => IO.succeed(NoContent(error)),
success => fetchContent(success)
There are a number of useful methods on the ZIO data type for retrying failed effects.
The most basic of these is
ZIO#retry, which takes a
Schedule and returns a new effect that will retry the first effect if it fails, according to the specified policy:
val retriedOpenFile: ZIO[Clock, IOException, Array[Byte]] =
The next most powerful function is
ZIO#retryOrElse, which allows specification of a fallback to use, if the effect does not succeed with the specified policy:
(_, _) => ZIO.succeed(DefaultData))
The final method,
ZIO#retryOrElseEither, allows returning a different type for the fallback.
For more information on how to build schedules, see the documentation on Schedule.
If you are comfortable with basic error handling, then the next step is to learn about safe resource handling.