Parallel and Sequential Errors

On this page

In a typical Effect application, when an error occurs, it usually fails with the first error encountered by the Effect runtime. Let's look at an example:

ts
import { Effect } from "effect"
 
const fail = Effect.fail("Oh uh!")
const die = Effect.dieMessage("Boom!")
 
const program = Effect.all([fail, die]).pipe(
Effect.andThen(die),
Effect.asVoid
)
 
Effect.runPromiseExit(program).then(console.log)
/*
Output:
{
_id: 'Exit',
_tag: 'Failure',
cause: { _id: 'Cause', _tag: 'Fail', failure: 'Oh uh!' }
}
*/
ts
import { Effect } from "effect"
 
const fail = Effect.fail("Oh uh!")
const die = Effect.dieMessage("Boom!")
 
const program = Effect.all([fail, die]).pipe(
Effect.andThen(die),
Effect.asVoid
)
 
Effect.runPromiseExit(program).then(console.log)
/*
Output:
{
_id: 'Exit',
_tag: 'Failure',
cause: { _id: 'Cause', _tag: 'Fail', failure: 'Oh uh!' }
}
*/

In this case, the program will fail with the first error, which is "Oh uh!":

Parallel Errors

However, in some situations, you may encounter multiple errors, especially when performing parallel computations. When parallel computations are involved, the application may fail due to multiple errors. Here's an example:

ts
import { Effect } from "effect"
 
const fail = Effect.fail("Oh uh!")
const die = Effect.dieMessage("Boom!")
 
const program = Effect.all([fail, die], { concurrency: "unbounded" }).pipe(
Effect.asVoid
)
 
Effect.runPromiseExit(program).then(console.log)
/*
Output:
{
_id: 'Exit',
_tag: 'Failure',
cause: {
_id: 'Cause',
_tag: 'Parallel',
left: { _id: 'Cause', _tag: 'Fail', failure: 'Oh uh!' },
right: { _id: 'Cause', _tag: 'Die', defect: [Object] }
}
}
*/
ts
import { Effect } from "effect"
 
const fail = Effect.fail("Oh uh!")
const die = Effect.dieMessage("Boom!")
 
const program = Effect.all([fail, die], { concurrency: "unbounded" }).pipe(
Effect.asVoid
)
 
Effect.runPromiseExit(program).then(console.log)
/*
Output:
{
_id: 'Exit',
_tag: 'Failure',
cause: {
_id: 'Cause',
_tag: 'Parallel',
left: { _id: 'Cause', _tag: 'Fail', failure: 'Oh uh!' },
right: { _id: 'Cause', _tag: 'Die', defect: [Object] }
}
}
*/

In this example, the program runs both fail and die concurrently, and if both fail, it will result in multiple errors.

parallelErrors

Effect provides a useful combinator called Effect.parallelErrors that exposes all parallel failure errors in the error channel. Here's how you can use it:

ts
import { Effect } from "effect"
 
const fail1 = Effect.fail("Oh uh!")
const fail2 = Effect.fail("Oh no!")
const die = Effect.dieMessage("Boom!")
 
const program = Effect.all([fail1, fail2, die], {
concurrency: "unbounded"
}).pipe(Effect.asVoid, Effect.parallelErrors)
 
Effect.runPromiseExit(program).then(console.log)
/*
Output:
{
_id: 'Exit',
_tag: 'Failure',
cause: { _id: 'Cause', _tag: 'Fail', failure: [ 'Oh uh!', 'Oh no!' ] }
}
*/
ts
import { Effect } from "effect"
 
const fail1 = Effect.fail("Oh uh!")
const fail2 = Effect.fail("Oh no!")
const die = Effect.dieMessage("Boom!")
 
const program = Effect.all([fail1, fail2, die], {
concurrency: "unbounded"
}).pipe(Effect.asVoid, Effect.parallelErrors)
 
Effect.runPromiseExit(program).then(console.log)
/*
Output:
{
_id: 'Exit',
_tag: 'Failure',
cause: { _id: 'Cause', _tag: 'Fail', failure: [ 'Oh uh!', 'Oh no!' ] }
}
*/

In this example, Effect.parallelErrors combines the errors from fail1 and fail2 into a single error.

Note that this operator is only for failures, not defects or interruptions.

Sequential Errors

When working with resource-safety operators like Effect.ensuring, you may encounter multiple sequential errors. This happens because regardless of whether the original effect has any errors or not, the finalizer is uninterruptible and will run. Here's an example:

ts
import { Effect } from "effect"
 
const fail = Effect.fail("Oh uh!")
const die = Effect.dieMessage("Boom!")
 
const program = fail.pipe(Effect.ensuring(die))
 
Effect.runPromiseExit(program).then(console.log)
/*
Output:
{
_id: 'Exit',
_tag: 'Failure',
cause: {
_id: 'Cause',
_tag: 'Sequential',
left: { _id: 'Cause', _tag: 'Fail', failure: 'Oh uh!' },
right: { _id: 'Cause', _tag: 'Die', defect: [Object] }
}
}
*/
ts
import { Effect } from "effect"
 
const fail = Effect.fail("Oh uh!")
const die = Effect.dieMessage("Boom!")
 
const program = fail.pipe(Effect.ensuring(die))
 
Effect.runPromiseExit(program).then(console.log)
/*
Output:
{
_id: 'Exit',
_tag: 'Failure',
cause: {
_id: 'Cause',
_tag: 'Sequential',
left: { _id: 'Cause', _tag: 'Fail', failure: 'Oh uh!' },
right: { _id: 'Cause', _tag: 'Die', defect: [Object] }
}
}
*/

In this case, the program will result in multiple sequential errors if both fail and the finalizer die encounter errors.