Sequential and Parallel Errors
A simple and regular ZIO application usually fails with one error, which is the first error encountered by the ZIO runtime:
import zio._
object MainApp extends ZIOAppDefault {
val fail = ZIO.fail("Oh uh!")
val die = ZIO.dieMessage("Boom!")
val interruption = ZIO.interrupt
def run = (fail <*> die) *> interruption
}
This application will fail with the first error which is "Oh uh!":
timestamp=2022-03-09T09:50:22.067072131Z level=ERROR thread=#zio-fiber-0 message="Exception in thread "zio-fiber-2" java.lang.String: Oh uh!
at <empty>.MainApp.fail(MainApp.scala:4)"
In some cases, we may run into multiple errors. When we perform parallel computations, the application may fail due to multiple errors:
import zio._
object MainApp extends ZIOAppDefault {
def run = ZIO.fail("Oh!") <&> ZIO.fail("Uh!")
}
If we run this application, we can see two exceptions in two different fibers that caused the failure (zio-fiber-0 and zio-fiber-14):
timestamp=2022-03-09T08:05:48.703035927Z level=ERROR thread=#zio-fiber-0 message="Exception in thread "zio-fiber-13" java.lang.String: Oh!
at <empty>.MainApp.run(MainApp.scala:4)
Exception in thread "zio-fiber-14" java.lang.String: Uh!
at <empty>.MainApp.run(MainApp.scala:4)"
ZIO has a combinator called ZIO#parallelErrors that exposes all parallel failure errors in the error channel:
import zio._
val result: ZIO[Any, ::[String], Nothing] =
(ZIO.fail("Oh uh!") <&> ZIO.fail("Oh Error!")).parallelErrors
Note that this operator is only for failures, not defects or interruptions.
Resource-safety operators like ZIO#ensuring can also produce multiple sequential errors. Regardless of whether the original effect succeeds or fails, the finalizer always runs and is uninterruptible. If the finalizer is not an unexceptional effect (URIO) and it encounters a defect, both the original error and the finalizer's defect are recorded — creating multiple sequential errors:
import zio._
object MainApp extends ZIOAppDefault {
def run = ZIO.fail("Oh uh!").ensuring(ZIO.dieMessage("Boom!"))
}
When we run this application, we can see that the original failure (Oh uh!) was suppressed by another defect (Boom!):
timestamp=2022-03-09T08:30:56.563179230Z level=ERROR thread=#zio-fiber-0 message="Exception in thread "zio-fiber-2" java.lang.String: Oh uh!
at <empty>.MainApp.run(MainApp.scala:4)
Suppressed: java.lang.RuntimeException: Boom!
at <empty>.MainApp.run(MainApp.scala:4)"