Scopes release resources when work ends

A Scope owns resource cleanup. A fiber is an independently running Effect. Acquisition obtains a resource; release cleans it up.

Construct the resource inside an acquisition Effect. Register release before handing the resource to its caller. Keep use inside the resource’s lifetime.

Acquire, use, and release one resource

The migration adapter opens a Postgres client inside Effect.tryPromise. acquireUseRelease gives the client to migration work and then runs shutdown.

packages/database/src/postgres-migrations.ts · 65e9465f
              return yield* Effect.acquireUseRelease(
                Effect.tryPromise({
                  catch: (cause) =>
                    new PostgresMigrationConnectionError({ cause }),
                  // @effect-diagnostics-next-line asyncFunction:off -- The pg client exposes Promise-based connection setup.
                  try: async () => {
                    const connection = new Client({ connectionString: url });

                    await connection.connect();

                    return connection;
                  },
                }),
                (connection) =>
                  runPostgresMigrations(
                    input.directory,
                    makePgMigrationExecutor(connection)
                  ),

The release callback closes the connection:

packages/database/src/postgres-migrations.ts · 65e9465f
                (connection) =>
                  Effect.tryPromise({
                    catch: (cause) =>
                      new PostgresMigrationConnectionError({ cause }),
                    // @effect-diagnostics-next-line asyncFunction:off -- The pg client exposes Promise-based shutdown.
                    try: async () => {
                      await connection.end();
                    },
                  }).pipe(Effect.orDie)

This example handles one acquired connection. If acquisition fails before release registration, the acquisition code must clean up any partial resource it created.

Choose the lifetime

A resource finalizer is cleanup work attached to an owner. It is not a business-state transition.

Use acquireRelease when several operations need the same resource. A service Layer can own that resource for its build lifetime. Callers must not retain the handle after its Scope closes.

Prove all use exits

The checked fixture creates a counted handle inside Effect.sync. Its finalizer increments a separate release counter.

packages/core/test/scoped-resource.test.ts · 4019cffb
      const acquired = yield* Ref.make(0);
      const released = yield* Ref.make(0);
      const started = yield* Deferred.make<boolean>();

      const acquire = Effect.acquireRelease(
        Effect.sync(() => ({ name: "counted-resource" })).pipe(
          Effect.tap(() => Ref.update(acquired, (count) => count + 1)),
          Effect.tap(() => Deferred.succeed(started, true))
        ),
        () => Ref.update(released, (count) => count + 1)
      );

The test cases exercise success, expected failure39, and interruption. Each case must acquire once and release once. It also checks that the selected exit actually occurred.

packages/core/test/scoped-resource.test.ts · 4019cffb
      if (outcome === "interruption") {
        const fiber = yield* Effect.forkChild(use);
        yield* Deferred.await(started);
        yield* Fiber.interrupt(fiber);
        const exit = yield* Fiber.await(fiber);
        expect(Exit.isFailure(exit) && Cause.hasInterrupts(exit.cause)).toBe(
          true
        );
      } else {
        const result = yield* Effect.result(use);
        expect(Result.isFailure(result)).toBe(outcome === "failure");

        if (Result.isFailure(result)) {
          expect(result.failure).toBe("expected-refusal");
        }
      }

      expect(yield* Ref.get(acquired)).toBe(1);
      expect(yield* Ref.get(released)).toBe(1);

Interrupting a fiber does not bypass its registered finalizer.

Common mistakes

The migration adapter makes shutdown failure a defect with orDie. Ordinary callers still need declared failures in the typed channel.

Effect idiom and house rule

Start with Layer composition18 when choosing the resource’s owner. Then read concurrent work23 before starting child work.

A Scope ends with its process. Steady state recycles resources9 covers growth that outlives one Scope.

Return to the Effect reading order29.

Sources

  1. Effect contributors. 2026. Effect. Effect 4.0.0.. Effect-TS. acquireRelease registers release after successful acquisition; scoped supplies and closes a Scope; acquireUseRelease brackets one use. Accessed 2026-10-06.
  2. Effect contributors. 2026. Acquiring resources with Effect.acquireRelease.. Effect-TS. Construct resources inside the acquisition Effect and install their finalizers in a service Layer. Accessed 2026-10-06.
  3. rat-stack contributors. 2026. Postgres migration resource ownership.. rat-stack. The migration adapter constructs and connects the client inside acquisition, uses it, then closes it. Accessed 2026-10-06.
  4. rat-stack contributors. 2026. Scoped resource exit fixture.. rat-stack. Counts one acquisition and one release after successful use, expected failure, and fiber interruption. Accessed 2026-10-06.
  5. Langton, Kit. 2026. Testing. Effect Solutions.. Kit Langton. Scoped test resources supply a useful ownership question; this guide uses independent rat-stack examples and exact Effect source. Accessed 2026-10-06.

Last changed 2026-10-24 in abefe39. Source on GitHub. Change log.

Linked from

  1. add-a-feature-flag. Declare a typed feature flag, its scoped evidence ladder, approval roles, removal date, and durable ledger adapter.

    Omission means the literal global scope.¶

  2. Agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.

    Scopes release resources when work ends — Acquire resources lazily, keep their use inside the owner, and release them after success, failure, or interruption.

  3. Agent rules for rat-stack (AGENTS.md). What you may change, which commands to run, and which changes need approval.

    Child configs may add exact allowedUnstableApis only for modules warned about in that scope.

  4. Build and run rat-stack (README.md). What is in the repo, how the example works, and how to run it.

    The model gets search for ranked matches with TypeScript signatures and execute for JavaScript with tools in scope.

  5. Bulkheads isolate scarce capacity. Reserve independent capacity for workloads that must not starve each other.

    Connect this to the existing rat-stack guidance.¶

  6. Change log. What changed in the files served here, newest first.

    Scopes release resources when work ends — edited: fix(site): replace published Markdown tables with lists and fence reader output

  7. Code snippets quote a pinned source. Shipped design brief: pinned Git objects, typed diagnostics, and one build-only snippet model for both views.

    A Scope owns resource cleanup.¶

  8. Concurrent work needs an owner and a bound. Limit active work, retain result order, and stop child work when its owning lifetime ends.

    The sandbox writes queued input to a child process through a scoped fiber: child work that stops when its owning Scope closes.

  9. durability. Keep a rat-stack app standing when a provider, pool, or dependency slows down or a worker dies. Use at any I/O boundary, connection pool, provider call, shared waiter, fan-out, flag, gate, or work that can outlive a request, or when asked "what happens when this is slow", "add a timeout", "make this durable", or "what survives a crash".

    Scope waiters to the request Principle: principles/scope-waiters-to-the-request.md¶

  10. Durability. Seven durable Effect patterns, each with its seam, its fence, and the incident that taught it.

    Primitive: a Deferred, Cache, Ref, Semaphore, Queue, Latch, PubSub, PartitionedSemaphore, or TxSemaphore created inside one request or runtime scope.¶

  11. Durability bibliography. Books, checked short quotations, source limits, and Effect mapping provenance.

    Designing Data-Intensive Applications and Newman’s Building Microservices gave only fragments for their scope.¶

  12. Durability starts with a recovery record. A reading order for storage, replay, worker ownership, overload, and recovery tests.

    Effect is not BEAM: Fibers share a runtime; scope ownership does not supply isolated heaps or OTP restart strategies.¶

  13. Effect + Alchemy peer patterns. Source-grounded patterns from repos on nearby Effect and Alchemy pins.

    Service turns it into module- scope query and mutation atoms for React.

  14. Effect 4 study: September 2026. Dated Effect 4 source studies from September 2026; current versions live in pins.md.

    org/@xstate%2feffect, a first-publish scope problem.

  15. Effect basics. An Effect describes work, expected failures, and required services before a runtime executes it.

    Scopes: acquire resources inside their owner and release them on exit.¶

  16. Effect is not BEAM. Fibers share a runtime; scope ownership does not supply isolated heaps or OTP restart strategies.

    Scope and FiberSet own local lifetimes.

  17. Every task needs an owner. Give child work a lifetime, a failure observer, and a recovery decision.

    forkScoped and FiberSet attach work to a Scope.

  18. Feature flags. Typed feature flags with owners, removal dates, and stable subject rollouts.

    record_flag_evidence records a measurement at the current rung and exact scope.¶

  19. Flags climb on evidence. A flag ladder connects each rollout step to scoped measurements, approval, and a durable receipt.

    The child’s readScope schema defines the flag’s scope.

  20. Full agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
  21. Lifecycles are machines. XState owns finite lifecycle states while declared Effect actors own side effects.

    A scope owns cleanup when that work ends.¶

  22. Lint and type escape ledger (debt.md). A source-linked count of repo-owned lint and type escapes.

    Reason: The SDK-erased registered exporter is rebuilt by WorkerBridge inside each event scope.

  23. Oxlint rule limits. How the current lint rules draw their syntax boundaries.
    Link context

    no-browser-globals-on-server: Covered now: Bare runtime references, literal/template member keys, globalThis/self/global/window, unchanged aliases, and destructuring are checked with value- scope awareness.

  24. Purpose and boundaries of rat-stack (VISION.md). What this starter is for and what a useful copy should keep.

    Scope: joelhooks/rat-stack, the reference repo, and the shape a project inherits when it vendors or clones it.

  25. Rat Stack lore | rat-stack. Short, source-grounded notes on the ideas and decisions behind rat-stack.

    Scopes release resources when work ends — Acquire resources lazily, keep their use inside the owner, and release them after success, failure, or interruption.

  26. rat-stack-mode. Route non-trivial rat-stack code, data, design, investigation, and documentation tasks to every matching playbook and principle.

    md sets the intent, reliability goals, scope, package-removal test, and shrinking debt.

  27. Retry load needs a budget. One retry owner must control attempts, delay, and aggregate load.

    Shared budgets require an explicit owner and accounting scope.¶

  28. Run Effect at the integration boundary. Build the runtime where a calling framework enters, reuse its services, and dispose it when that integration ends.

    Read scopes when deciding what the integration owns.

  29. ship. Learn how to ship a change through merge, CI, release, stage deployment, rollout, verification, rollback and flags.

    WAIT[release- scope]: Authorization and the target stage are known.¶

  30. Source change history (log.md). The generated change log as Markdown.

    Scopes release resources when work ends — edited: fix(site): replace published Markdown tables with lists and fence reader output

  31. Steady state recycles resources. Every accumulation needs a deliberate recycling policy.

    Cache and Scope cover runtime lifetime.

  32. Supervision chooses what restarts. Restart policy follows dependency shape and bounds repeated recovery.

    Scope and FiberSet handle lifetime.

  33. Tests prove behavior through supplied services. Supply services through Layers, choose properties or models for domain rules, and reject tests that cannot detect broken behavior.

    A Scope owns cleanup.

  34. uncomplect. Find responsibilities that a rat-stack design combines, separate them into capabilities, cartridges, machines, features, and clients, and enforce that separation through types and checks. Use when asked to simplify, review, or replace a design, "what would Rich Hickey do", "optimize for deletion", or "define this error out of existence".

    A held connection, a request, and a provider call in one scope is a braid.

Unlinked mentions (12)

Content updated .