Skip to content

Sandboxing

Errors are an inevitable part of programming, and they can arise from various sources like failures, defects, fiber interruptions, or combinations of these. This guide explains how to use the Effect.sandbox function to isolate and understand the causes of errors in your Effect-based code.

The Effect.sandbox function allows you to encapsulate all the potential causes of an error in an effect. It exposes the full cause of an effect, whether it’s due to a failure, defect, fiber interruption, or a combination of these factors.

In simple terms, it takes an effect Effect<A, E, R> and transforms it into an effect Effect<A, Cause<E>, R> where the error channel now contains a detailed cause of the error.

Syntax

Effect<A, E, R> -> Effect<A, Cause<E>, R>

By using the Effect.sandbox function, you gain access to the underlying causes of exceptional effects. These causes are represented as a type of Cause<E> and are available in the error channel of the Effect data type.

Once you have exposed the causes, you can utilize standard error-handling operators like Effect.catchAll and Effect.catchTags to handle errors more effectively. These operators allow you to respond to specific error conditions.

If needed, we can undo the sandboxing operation with Effect.unsandbox.

Example (Handling Different Error Causes)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Console
Console
} from "effect"
// ┌─── Effect<string, Error, never>
// ▼
const
const task: Effect.Effect<string, Error, never>
task
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fail: <Error>(error: Error) => Effect.Effect<never, Error, never>

Creates an Effect that represents a recoverable error.

When to Use

Use this function to explicitly signal an error in an Effect. The error will keep propagating unless it is handled. You can handle the error with functions like

catchAll

or

catchTag

.

@seesucceed to create an effect that represents a successful value.

@example

// Title: Creating a Failed Effect
import { Effect } from "effect"
// ┌─── Effect<never, Error, never>
// ▼
const failure = Effect.fail(
new Error("Operation failed due to network error")
)

@since2.0.0

fail
(new
var Error: ErrorConstructor
new (message?: string) => Error
Error
("Oh uh!")).
Pipeable.pipe<Effect.Effect<never, Error, never>, Effect.Effect<string, Error, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<never, Error, never>) => Effect.Effect<string, Error, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const as: <string>(value: string) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<string, E, R> (+1 overload)

Replaces the value inside an effect with a constant value.

Details

This function allows you to ignore the original value inside an effect and replace it with a constant value.

When to Use

It is useful when you no longer need the value produced by an effect but want to ensure that the effect completes successfully with a specific constant result instead. For instance, you can replace the value produced by a computation with a predefined value, ignoring what was calculated before.

@example

// Title: Replacing a Value
import { pipe, Effect } from "effect"
// Replaces the value 5 with the constant "new value"
const program = pipe(Effect.succeed(5), Effect.as("new value"))
// Effect.runPromise(program).then(console.log)
// Output: "new value"

@since2.0.0

as
("primary result")
)
// ┌─── Effect<string, Cause<Error>, never>
// ▼
const
const sandboxed: Effect.Effect<string, Cause<Error>, never>
sandboxed
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const sandbox: <string, Error, never>(self: Effect.Effect<string, Error, never>) => Effect.Effect<string, Cause<Error>, never>

Transforms an effect to expose detailed error causes.

Details

This function enhances an effect by providing detailed information about any error, defect, or interruption that may occur during its execution. It modifies the error channel of the effect so that it includes a full cause of the failure, wrapped in a Cause<E> type.

After applying this function, you can use operators like

catchAll

and

catchTags

to handle specific types of errors.

If you no longer need the detailed cause information, you can revert the changes using

unsandbox

to return to the original error-handling behavior.

@seeunsandbox to restore the original error handling.

@example

import { Effect, Console } from "effect"
// ┌─── Effect<string, Error, never>
// ▼
const task = Effect.fail(new Error("Oh uh!")).pipe(
Effect.as("primary result")
)
// ┌─── Effect<string, Cause<Error>, never>
// ▼
const sandboxed = Effect.sandbox(task)
const program = Effect.catchTags(sandboxed, {
Die: (cause) =>
Console.log(`Caught a defect: ${cause.defect}`).pipe(
Effect.as("fallback result on defect")
),
Interrupt: (cause) =>
Console.log(`Caught a defect: ${cause.fiberId}`).pipe(
Effect.as("fallback result on fiber interruption")
),
Fail: (cause) =>
Console.log(`Caught a defect: ${cause.error}`).pipe(
Effect.as("fallback result on failure")
)
})
// Restore the original error handling with unsandbox
const main = Effect.unsandbox(program)
// Effect.runPromise(main).then(console.log)
// Output:
// Caught a defect: Oh uh!
// fallback result on failure

@since2.0.0

sandbox
(
const task: Effect.Effect<string, Error, never>
task
)
const
const program: Effect.Effect<string, Empty | Sequential<Error> | Parallel<Error>, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const catchTags: <never, Cause<Error>, string, {
Die: (cause: Die) => Effect.Effect<string, never, never>;
Interrupt: (cause: Interrupt) => Effect.Effect<string, never, never>;
Fail: (cause: Fail<...>) => Effect.Effect<...>;
}>(self: Effect.Effect<...>, cases: {
Die: (cause: Die) => Effect.Effect<string, never, never>;
Interrupt: (cause: Interrupt) => Effect.Effect<string, never, never>;
Fail: (cause: Fail<...>) => Effect.Effect<...>;
}) => Effect.Effect<...> (+1 overload)

Handles multiple errors in a single block of code using their _tag field.

When to Use

catchTags is a convenient way to handle multiple error types at once. Instead of using

catchTag

multiple times, you can pass an object where each key is an error type's _tag, and the value is the handler for that specific error. This allows you to catch and recover from multiple error types in a single call.

The error type must have a readonly _tag field to use catchTag. This field is used to identify and match errors.

@example

// Title: Handling Multiple Tagged Error Types at Once
import { Effect, Random } from "effect"
class HttpError {
readonly _tag = "HttpError"
}
class ValidationError {
readonly _tag = "ValidationError"
}
// ┌─── Effect<string, HttpError | ValidationError, never>
// ▼
const program = Effect.gen(function* () {
const n1 = yield* Random.next
const n2 = yield* Random.next
if (n1 < 0.5) {
yield* Effect.fail(new HttpError())
}
if (n2 < 0.5) {
yield* Effect.fail(new ValidationError())
}
return "some result"
})
// ┌─── Effect<string, never, never>
// ▼
const recovered = program.pipe(
Effect.catchTags({
HttpError: (_HttpError) =>
Effect.succeed(`Recovering from HttpError`),
ValidationError: (_ValidationError) =>
Effect.succeed(`Recovering from ValidationError`)
})
)

@since2.0.0

catchTags
(
const sandboxed: Effect.Effect<string, Cause<Error>, never>
sandboxed
, {
type Die: (cause: Die) => Effect.Effect<string, never, never>
Die
: (
cause: Die
cause
) =>
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>

@since2.0.0

log
(`Caught a defect: ${
cause: Die
cause
.
Die.defect: unknown
defect
}`).
Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<string, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<string, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const as: <string>(value: string) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<string, E, R> (+1 overload)

Replaces the value inside an effect with a constant value.

Details

This function allows you to ignore the original value inside an effect and replace it with a constant value.

When to Use

It is useful when you no longer need the value produced by an effect but want to ensure that the effect completes successfully with a specific constant result instead. For instance, you can replace the value produced by a computation with a predefined value, ignoring what was calculated before.

@example

// Title: Replacing a Value
import { pipe, Effect } from "effect"
// Replaces the value 5 with the constant "new value"
const program = pipe(Effect.succeed(5), Effect.as("new value"))
// Effect.runPromise(program).then(console.log)
// Output: "new value"

@since2.0.0

as
("fallback result on defect")
),
type Interrupt: (cause: Interrupt) => Effect.Effect<string, never, never>
Interrupt
: (
cause: Interrupt
cause
) =>
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>

@since2.0.0

log
(`Caught a defect: ${
cause: Interrupt
cause
.
Interrupt.fiberId: FiberId
fiberId
}`).
Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<string, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<string, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const as: <string>(value: string) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<string, E, R> (+1 overload)

Replaces the value inside an effect with a constant value.

Details

This function allows you to ignore the original value inside an effect and replace it with a constant value.

When to Use

It is useful when you no longer need the value produced by an effect but want to ensure that the effect completes successfully with a specific constant result instead. For instance, you can replace the value produced by a computation with a predefined value, ignoring what was calculated before.

@example

// Title: Replacing a Value
import { pipe, Effect } from "effect"
// Replaces the value 5 with the constant "new value"
const program = pipe(Effect.succeed(5), Effect.as("new value"))
// Effect.runPromise(program).then(console.log)
// Output: "new value"

@since2.0.0

as
("fallback result on fiber interruption")
),
type Fail: (cause: Fail<Error>) => Effect.Effect<string, never, never>
Fail
: (
cause: Fail<Error>
cause
) =>
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>

@since2.0.0

log
(`Caught a defect: ${
cause: Fail<Error>
cause
.
Fail<Error>.error: Error
error
}`).
Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<string, never, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<string, never, never>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const as: <string>(value: string) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<string, E, R> (+1 overload)

Replaces the value inside an effect with a constant value.

Details

This function allows you to ignore the original value inside an effect and replace it with a constant value.

When to Use

It is useful when you no longer need the value produced by an effect but want to ensure that the effect completes successfully with a specific constant result instead. For instance, you can replace the value produced by a computation with a predefined value, ignoring what was calculated before.

@example

// Title: Replacing a Value
import { pipe, Effect } from "effect"
// Replaces the value 5 with the constant "new value"
const program = pipe(Effect.succeed(5), Effect.as("new value"))
// Effect.runPromise(program).then(console.log)
// Output: "new value"

@since2.0.0

as
("fallback result on failure")
)
})
// Restore the original error handling with unsandbox
const
const main: Effect.Effect<string, Error, never>
main
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const unsandbox: <string, Error, never>(self: Effect.Effect<string, Cause<Error>, never>) => Effect.Effect<string, Error, never>

The unsandbox function is used to revert an effect that has been sandboxed by

sandbox

. When you apply unsandbox, the effect's error channel is restored to its original state, without the detailed Cause<E> information. This means that any underlying causes of errors, defects, or fiber interruptions are no longer exposed in the error channel.

This function is useful when you want to remove the detailed error tracking provided by sandbox and return to the standard error handling for your effect. Once unsandboxed, the effect behaves as if sandbox was never applied.

@seesandbox to expose the full cause of failures, defects, or interruptions.

@since2.0.0

unsandbox
(
const program: Effect.Effect<string, Empty | Sequential<Error> | Parallel<Error>, never>
program
)
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <string, Error>(effect: Effect.Effect<string, Error, never>, options?: {
readonly signal?: AbortSignal;
} | undefined) => Promise<string>

Executes an effect and returns the result as a Promise.

Details

This function runs an effect and converts its result into a Promise. If the effect succeeds, the Promise will resolve with the successful result. If the effect fails, the Promise will reject with an error, which includes the failure details of the effect.

The optional options parameter allows you to pass an AbortSignal for cancellation, enabling more fine-grained control over asynchronous tasks.

When to Use

Use this function when you need to execute an effect and work with its result in a promise-based system, such as when integrating with third-party libraries that expect Promise results.

@seerunPromiseExit for a version that returns an Exit type instead of rejecting.

@example

// Title: Running a Successful Effect as a Promise
import { Effect } from "effect"
// Effect.runPromise(Effect.succeed(1)).then(console.log)
// Output: 1

@example

//Example: Handling a Failing Effect as a Rejected Promise import { Effect } from "effect"

// Effect.runPromise(Effect.fail("my error")).catch(console.error) // Output: // (FiberFailure) Error: my error

@since2.0.0

runPromise
(
const main: Effect.Effect<string, Error, never>
main
).
Promise<string>.then<void, never>(onfulfilled?: ((value: string) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
(
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
globalThis.Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
)
/*
Output:
Caught a defect: Oh uh!
fallback result on failure
*/