Exit and Cause
When you run an Effect<A, E>, the outcome is an Exit<A, E>: either a
Success holding a value of type A, or a Failure holding a Cause<E>. The
Cause is the richer half of the story — an effect can fail in more ways than a
single E captures, and Cause records all of them: expected failures,
unexpected defects, fiber interruptions, and combinations of these.
import { Cause, Effect, Exit } from "effect"
const program = Effect.fail("boom")
// runPromiseExit never rejects — it resolves with an Exit describing the outcomeEffect.runPromiseExit(program).then((exit) => { const message = Exit.match(exit, { onSuccess: (value) => `succeeded with ${value}`, // The failure branch receives a Cause, not a bare error onFailure: (cause) => `failed:\n${Cause.pretty(cause)}` }) console.log(message)})// failed:// boomRunning an effect with the *Exit variants (runPromiseExit, runSyncExit)
gives you the result as data instead of as a thrown exception or rejected
promise — ideal for inspecting failures in tests and at program boundaries.
An Exit<A, E> has two cases:
Exit.Success— carries avalue: A.Exit.Failure— carries acause: Cause<E>.
You usually obtain an Exit by running an effect, but you can also build one
directly:
import { Cause, Exit } from "effect"
const ok = Exit.succeed(42)// { _id: 'Exit', _tag: 'Success', value: 42 }
const failed = Exit.failCause(Cause.fail("Something went wrong"))// { _id: 'Exit', _tag: 'Failure', cause: ... }Use Exit.isSuccess / Exit.isFailure to narrow, or Exit.match to handle
both cases at once (as in the opening example). To pull out just the parts you
care about, Exit.getSuccess returns an Option<A> and Exit.findErrorOption
returns the first typed error as an Option<E>.
import { Effect, Exit, Option } from "effect"
const exit = Effect.runSyncExit(Effect.succeed(1))
console.log(Exit.getSuccess(exit)) // { _id: 'Option', _tag: 'Some', value: 1 }console.log(Option.isSome(Exit.getSuccess(exit))) // trueCause<E> is the failure payload of an Exit. Internally it is a list of
reasons, where each reason is one of three kinds:
Fail<E>— an expected, typed error produced byEffect.fail. The error is on.error.Die— a defect: an unexpected error (a thrown exception, a failed assertion). The value is on.defect. Defects are not part of theEtype.Interrupt— the fiber was interrupted. Carries the interrupting.fiberId.
Modelling failure as a list lets a Cause represent several failures at once —
for example a try body and its finally block both failing, or concurrent
fibers each failing.
import { Cause } from "effect"
const fail = Cause.fail("Oh no!") // expected errorconst die = Cause.die("Boom!") // defectconst interrupt = Cause.interrupt(123) // interruption
// Combine reasons into a single causeconst combined = Cause.combine(fail, die)console.log(combined.reasons.length) // 2Inspecting a Cause
Section titled “Inspecting a Cause”Rather than matching on the internal structure, use the focused accessors. They search across all reasons for you:
import { Cause } from "effect"
// A cause holding both a typed failure and a defectconst cause = Cause.combine(Cause.fail("error 1"), Cause.die("defect"))
// Did it contain a typed failure? An unexpected defect?console.log(Cause.hasFails(cause)) // trueconsole.log(Cause.hasDies(cause)) // trueconsole.log(Cause.findError(cause)) // first typed error, as a Resultconsole.log(Cause.findDefect(cause)) // first defect, as a ResultIn real code the cause comes from running an effect — Exit.getCause returns
the Cause of a Failure (as an Option):
import { Cause, Effect, Exit, Option } from "effect"
const exit = Effect.runSyncExit(Effect.fail("boom"))
Option.match(Exit.getCause(exit), { onNone: () => console.log("the effect succeeded"), onSome: (cause) => console.log(Cause.pretty(cause)) // "boom"})Common accessors:
Cause.hasFails/Cause.hasDies/Cause.hasInterrupts— predicates.Cause.findError(cause)— the first typed error, as aResult<E, Cause<never>>.Cause.findErrorOption(cause)— the first typed error, as anOption<E>.Cause.findDefect(cause)— the first defect, as aResult<unknown, Cause<E>>.Cause.interruptors(cause)— the set of fiber ids that caused interruption.
Pretty printing
Section titled “Pretty printing”Cause.pretty formats a cause into a readable, multi-line string — exactly what
you want in logs and error reports:
import { Cause } from "effect"
console.log(Cause.pretty(Cause.fail("connection refused")))// connection refused
console.log(Cause.pretty(Cause.combine(Cause.fail("e1"), Cause.fail("e2"))))// e1// e2Where these fit
Section titled “Where these fit”You rarely build Exit or Cause values by hand. Instead you encounter them
when:
- running an effect with
runSyncExit/runPromiseExit, - inspecting failures in Testing,
- recovering from all failure modes (including defects) with the catch-all combinators described in Error Management,
- writing finalizers in Resource Management, whose
cleanup logic receives the
Exitof the scoped effect.
For ordinary, synchronous success-or-failure in pure code — without defects or
interruptions — reach for Result instead.