Skip to content
Effect Days 2026 Get your ticket

FiberHandle

Manages at most one fiber inside a scope.

A FiberHandle<A, E> can hold one Fiber<A, E>. Installing a new fiber interrupts the previous one unless the operation is configured with onlyIfMissing, and closing the owning scope interrupts the current fiber. This module includes constructors for handles and scoped runtimes, helpers for setting, reading, clearing, and running fibers, and operations for joining the current fiber or waiting until the handle is empty.

15 exports Added in v2.0.0 Source

Combinators

awaitEmpty

Added in v3.13.0 Source

Waits for the fiber in the FiberHandle to complete.

Signature

declare function awaitEmpty<A, E>(self: FiberHandle<A, E>): Effect<void, E>

Example

(Waiting for a fiber to complete)

import { Effect, FiberHandle, Option } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
yield* FiberHandle.run(handle, Effect.yieldNow)
// Wait for the fiber to complete
yield* FiberHandle.awaitEmpty(handle)
return yield* FiberHandle.get(handle)
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => Option.none()

clear

Added in v2.0.0 Source

Interrupts the fiber currently stored in the FiberHandle, if any, and leaves the handle empty.

Signature

declare function clear<A, E>(self: FiberHandle<A, E>): Effect<void>

Example

(Clearing a fiber handle)

import { Effect, FiberHandle, Option } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
// Add a fiber
yield* FiberHandle.run(handle, Effect.never)
// Clear the handle, interrupting the fiber
yield* FiberHandle.clear(handle)
// The handle is now empty
return FiberHandle.getUnsafe(handle)
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => Option.none()

get

Added in v2.0.0 Source

Retrieves the fiber from the FiberHandle effectfully.

Signature

declare function get<A, E>(self: FiberHandle<A, E>): Effect<Option<Fiber<A, E>>>

Example

(Reading the current fiber)

import { Effect, FiberHandle, Option } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
// Add a fiber
yield* FiberHandle.run(handle, Effect.never)
// Get the current fiber if present
const fiber = yield* FiberHandle.get(handle)
return Option.map(fiber, () => true)
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => Option.some(true)

getUnsafe

Added in v4.0.0 Source

Retrieves the fiber from the FiberHandle synchronously.

When to use

Use when synchronous inspection of the current fiber is needed and an Option result is enough outside the Effect workflow.

Signature

declare function getUnsafe<A, E>(self: FiberHandle<A, E>): Option<Fiber<A, E>>

Example

(Reading the current fiber unsafely)

import { Effect, FiberHandle, Option } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
// No fiber initially
const emptyFiber = FiberHandle.getUnsafe(handle)
// Add a fiber
yield* FiberHandle.run(handle, Effect.never)
const fiber = FiberHandle.getUnsafe(handle)
return [emptyFiber, Option.map(fiber, () => true)]
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => [Option.none(), Option.some(true)]

join

Added in v2.0.0 Source

Waits for the FiberHandle to fail or close.

Details

The returned Effect fails with the first managed fiber failure that is not ignored by the handle's interruption rules. Normal successful completion of a managed fiber only removes it from the handle; use awaitEmpty to wait for the current fiber to finish.

Signature

declare function join<A, E>(self: FiberHandle<A, E>): Effect<void, E>

Example

(Propagating fiber failures)

import { Effect, Exit, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
yield* FiberHandle.set(handle, Effect.runFork(Effect.fail("error")))
// parent fiber will fail with "error"
yield* FiberHandle.join(handle)
})
const actual = await Effect.runPromise(Effect.exit(Effect.scoped(program)))
actual // => Exit.fail("error")

run

Added in v2.0.0 Source

Forks an Effect and stores the resulting fiber in the FiberHandle.

Details

The handle manages only one fiber: running a new effect interrupts the previous fiber unless onlyIfMissing is set. When the managed fiber completes, it is removed from the handle.

Signature

declare const run: {
<A, E>(self: FiberHandle<A, E>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
readonly startImmediately?: boolean;
}): <R, XE, XA>(effect: Effect<XA, XE, R>) => Effect<Fiber<XA, XE>, never, R>;
<A, E, R, XE, XA>(self: FiberHandle<A, E>, effect: Effect<XA, XE, R>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
readonly startImmediately?: boolean;
}): Effect<Fiber<XA, XE>, never, R>;
}

Example

(Running an effect in a fiber handle)

import { Effect, Fiber, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
// Run an effect and get the fiber
const fiber = yield* FiberHandle.run(handle, Effect.succeed("hello"))
const result = yield* Fiber.join(fiber)
// Running another effect will interrupt the previous one
const fiber2 = yield* FiberHandle.run(handle, Effect.succeed("world"))
const result2 = yield* Fiber.join(fiber2)
return [result, result2]
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => ["hello", "world"]

runtime

Added in v2.0.0 Source

Captures the current runtime and returns a function for forking effects into an existing FiberHandle.

Details

Each call returns the forked fiber, stores it in the handle, and interrupts the previous fiber unless onlyIfMissing is set.

Signature

declare const runtime: <A, E>(self: FiberHandle<A, E>) => <R = never>() => Effect.Effect<<XE extends E, XA extends A>(effect: Effect.Effect<XA, XE, R>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
readonly scheduler?: Scheduler;
readonly signal?: AbortSignal;
}) => Fiber.Fiber<XA, XE>, never, R>

Example

(Capturing a runtime for fiber handles)

import { Context, Effect, Fiber, FiberHandle } from "effect"
class Users extends Context.Service<Users, {
readonly getAll: Effect.Effect<Array<unknown>>
}>()("Users") {}
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
const run = yield* FiberHandle.runtime(handle)<Users>()
// run an effect and set the fiber in the handle
const fiberA = run(Effect.andThen(Users, (_) => _.getAll))
// this will interrupt the previous fiber
const fiberB = run(Effect.andThen(Users, (_) => _.getAll))
yield* Fiber.await(fiberA)
return (yield* Fiber.join(fiberB)).length
}).pipe(
Effect.scoped // The fiber will be interrupted when the scope is closed
)
const actual = await Effect.runPromise(Effect.provideService(program, Users, {
getAll: Effect.succeed([])
}))
actual // => 0

runtimePromise

Added in v3.13.0 Source

Captures the current runtime and returns a function for running effects in an existing FiberHandle as Promises.

Details

Each call stores the forked fiber in the handle and interrupts the previous fiber unless onlyIfMissing is set. The Promise resolves with the effect's success value or rejects with the squashed failure cause.

Signature

declare function runtimePromise<A, E>(self: FiberHandle<A, E>): <R = never>() => Effect<<XE, XA>(effect: Effect<XA, XE, R>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
readonly scheduler?: Scheduler;
readonly signal?: AbortSignal;
}) => Promise<XA>, never, R>

Example

(Capturing a runtime for promises)

import { Effect, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
const runPromise = yield* FiberHandle.runtimePromise(handle)<never>()
// Run an effect and get a promise
const promise = runPromise(Effect.succeed("hello"))
return yield* Effect.promise(() => promise)
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => "hello"

set

Added in v2.0.0 Source

Sets the fiber in the FiberHandle.

Details

When the fiber completes, it will be removed from the FiberHandle. If a fiber already exists in the FiberHandle, it will be interrupted unless options.onlyIfMissing is set.

Signature

declare const set: {
<A, E, XE, XA>(fiber: Fiber<XA, XE>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
}): (self: FiberHandle<A, E>) => Effect<void>;
<A, E, XE, XA>(self: FiberHandle<A, E>, fiber: Fiber<XA, XE>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
}): Effect<void>;
}

Example

(Setting a fiber safely)

import { Effect, Fiber, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
const fiber = Effect.runFork(Effect.succeed("hello"))
// Set the fiber safely
yield* FiberHandle.set(handle, fiber)
// The fiber is now managed by the handle
return yield* Fiber.join(fiber)
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => "hello"

setUnsafe

Added in v4.0.0 Source

Sets the fiber in a FiberHandle. When the fiber completes, it will be removed from the FiberHandle. If a fiber is already running, it will be interrupted unless options.onlyIfMissing is set.

When to use

Use when an existing forked fiber must be installed synchronously into a handle and immediate interruption of replaced or closed fibers is acceptable.

Signature

declare const setUnsafe: {
<A, E, XE, XA>(fiber: Fiber<XA, XE>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
}): (self: FiberHandle<A, E>) => void;
<A, E, XE, XA>(self: FiberHandle<A, E>, fiber: Fiber<XA, XE>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
}): void;
}

Example

(Setting a fiber unsafely)

import { Effect, Fiber, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
const fiber = Effect.runFork(Effect.succeed("hello"))
// Set the fiber directly (unsafe)
FiberHandle.setUnsafe(handle, fiber)
// The fiber is now managed by the handle
return yield* Fiber.join(fiber)
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => "hello"

Constructors

make

Added in v2.0.0 Source

Creates a scoped FiberHandle that can store a single fiber.

Details

When the associated Scope is closed, the contained fiber will be interrupted. You can add a fiber to the handle using FiberHandle.run, and the fiber will be automatically removed from the FiberHandle when it completes.

Signature

declare function make<A = unknown, E = unknown>(): Effect<FiberHandle<A, E>, never, Scope>

Example

(Creating a scoped fiber handle)

import { Effect, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
// run some effects
yield* FiberHandle.run(handle, Effect.never)
// this will interrupt the previous fiber
yield* FiberHandle.run(handle, Effect.never)
yield* Effect.yieldNow
return handle.state._tag === "Open" && handle.state.fiber !== undefined
}).pipe(
Effect.scoped // The fiber will be interrupted when the scope is closed
)
const actual = await Effect.runPromise(program)
actual // => true

makeRuntime

Added in v2.0.0 Source

Creates a scoped run function that forks effects into a new FiberHandle.

Details

Each call returns the forked fiber, stores it in the handle, and interrupts the previous fiber unless onlyIfMissing is set. The managed fiber is interrupted when the handle's scope closes.

Signature

declare function makeRuntime<R, E = unknown, A = unknown>(): Effect<<XE, XA>(effect: Effect<XA, XE, R>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
readonly scheduler?: Scheduler;
readonly signal?: AbortSignal;
}) => Fiber<XA, XE>, never, Scope | R>

Example

(Running effects with a fiber handle)

import { Cause, Effect, Exit, Fiber, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const run = yield* FiberHandle.makeRuntime<never>()
// Run effects and get fibers back
const fiberA = run(Effect.never)
const fiberB = run(Effect.succeed("second"))
// The second fiber will interrupt the first
const resultA = yield* Fiber.await(fiberA)
const resultB = yield* Fiber.await(fiberB)
return [resultA, resultB]
}).pipe(Effect.scoped)
const actual = await Effect.runPromise(program)
actual // => [Exit.failCause(Cause.interrupt(-1)), Exit.succeed("second")]

Creates a scoped run function that forks effects into a new FiberHandle and returns a Promise for each effect result.

When to use

Use when integrating a scoped FiberHandle runner with Promise-based APIs and Promise rejection from squashed failures is the desired boundary.

Details

Each call stores the fiber in the handle and interrupts the previous fiber unless onlyIfMissing is set. The returned Promise resolves with the effect's success value or rejects with the squashed failure cause.

Signature

declare function makeRuntimePromise<R = never, A = unknown, E = unknown>(): Effect<<XE, XA>(effect: Effect<XA, XE, R>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
readonly scheduler?: Scheduler;
readonly signal?: AbortSignal;
}) => Promise<XA>, never, Scope | R>

Example

(Running effects as promises)

import { Effect, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const run = yield* FiberHandle.makeRuntimePromise()
// Run effects and get promises back
const promise = run(Effect.succeed("hello"))
return yield* Effect.promise(() => promise)
}).pipe(Effect.scoped)
const actual = await Effect.runPromise(program)
actual // => "hello"

Guards

Returns true if a value is a FiberHandle by checking for the FiberHandle runtime marker.

Signature

declare function isFiberHandle(u: unknown): u is FiberHandle<unknown, unknown>

Example

(Checking fiber handles)

import { Effect, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
return [FiberHandle.isFiberHandle(handle), FiberHandle.isFiberHandle("not a handle")]
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => [true, false]

Models

FiberHandle interface

Added in v2.0.0 Source

Scoped handle that manages at most one fiber, interrupts the current fiber when the handle's scope closes, and removes managed fibers from the handle when they complete.

Signature

interface FiberHandle<out A = unknown, out E = unknown> extends Pipeable, Inspectable {
readonly "~effect/FiberHandle": "~effect/FiberHandle";
readonly deferred: Deferred<void, unknown>;
state: {
readonly _tag: "Open";
fiber: Fiber<A, E> | undefined;
} | {
readonly _tag: "Closed";
};
}

Example

(Managing a single fiber)

import { Effect, Fiber, FiberHandle } from "effect"
const program = Effect.gen(function*() {
// Create a FiberHandle that can hold fibers producing strings
const handle = yield* FiberHandle.make<string, never>()
// The handle can store and manage a single fiber
const fiber = yield* FiberHandle.run(handle, Effect.succeed("hello"))
return yield* Fiber.join(fiber)
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => "hello"