Error Management



We can attempt one effect, and if it fails, we can try another effect using the Effect.orElse combinator:

import { Effect } from "effect"
const success = Effect.succeed("success")
const failure ="failure")
const fallback = Effect.succeed("fallback")
const program1 = Effect.orElse(success, () => fallback)
console.log(Effect.runSync(program1)) // Output: "success"
const program2 = Effect.orElse(failure, () => fallback)
console.log(Effect.runSync(program2)) // Output: "fallback"

orElseFail / orElseSucceed

These two operators modify the original failure by replacing it with constant succeed or failure values.

The Effect.orElseFail will always replace the original failure with the new one:

import { Effect } from "effect"
class NegativeAgeError {
  readonly _tag = "NegativeAgeError"
  constructor(readonly age: number) {}
class IllegalAgeError {
  readonly _tag = "IllegalAgeError"
  constructor(readonly age: number) {}
const validate = (
  age: number
): Effect.Effect<never, NegativeAgeError | IllegalAgeError, number> => {
  if (age < 0) {
    return NegativeAgeError(age))
  } else if (age < 18) {
    return IllegalAgeError(age))
  } else {
    return Effect.succeed(age)
// $ExpectType Effect<never, string, number>
const program1 = Effect.orElseFail(validate(3), () => "invalid age")

The Effect.orElseSucceed will always replace the original failure with a success value, so the resulting effect cannot fail:

// $ExpectType Effect<never, never, number>
const program2 = Effect.orElseSucceed(validate(3), () => 0)


The firstSuccessOf operator simplifies running a series of effects and returns the result of the first one that succeeds. If none of the effects succeed, the resulting effect will fail with the error of the last effect in the series.

This operator utilizes Effect.orElse to combine multiple effects into a single effect.

In the following example, we attempt to retrieve a configuration from different nodes. If retrieving from the master node fails, we successively try retrieving from the next available nodes until we find a successful result:

import { Effect, Console } from "effect"
interface Config {
  // ...
const makeConfig = (/* ... */): Config => ({})
const remoteConfig = (name: string): Effect.Effect<never, Error, Config> =>
  Effect.gen(function* (_) {
    if (name === "node3") {
      yield* _(Console.log(`Config for ${name} found`))
      return makeConfig()
    } else {
      yield* _(Console.log(`Unavailable config for ${name}`))
      return yield* _( Error()))
// $ExpectType Effect<never, Error, Config>
const masterConfig = remoteConfig("master")
// $ExpectType Effect<never, Error, Config>[]
const nodeConfigs = ["node1", "node2", "node3", "node4"].map(remoteConfig)
// $ExpectType Effect<never, Error, Config>
const config = Effect.firstSuccessOf([masterConfig, ...nodeConfigs])
Unavailable config for master
Unavailable config for node1
Unavailable config for node2
Config for node3 found

If the collection provided to the Effect.firstSuccessOf function is empty, it will throw an IllegalArgumentException error.