# Scopes release resources when work ends

> For agents: start with the [agent guide](https://ratstack.sh/llms.txt). Every page is Markdown by default; add `Accept: text/html` for HTML.

Free workshop: [how to burn a trillion tokens and get good results](/tokenmaxx#interested).

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

## Bracket one use

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 at 65e9465f38092e24486392f22c08f45d61230c20; lines 64-81; highlighted none.

```typescript
              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 at 65e9465f38092e24486392f22c08f45d61230c20; lines 82-90; highlighted none.

```typescript
                (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

| Operation           | Owner                                                  |
| ------------------- | ------------------------------------------------------ |
| `acquireUseRelease` | One use callback; release follows that callback's exit |
| `acquireRelease`    | The current Scope; release runs when that Scope closes |
| `scoped`            | A new Scope around the supplied Effect                 |

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 at 4019cffbcbdb282d6f006854ff5b1f26d3b407e0; lines 8-18; highlighted none.

```typescript
      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 matrix exercises success, expected failure, 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 at 4019cffbcbdb282d6f006854ff5b1f26d3b407e0; lines 37-55; highlighted none.

```typescript
      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

- Allocate the client before building the acquisition Effect. An interruption can leave that client without a registered finalizer.
- Return a resource from `scoped` and use it later. Its lifetime has already ended.
- Claim failed acquisition always invokes release. Release registration requires successful acquisition.
- Treat a close failure as harmless without checking the adapter's failure policy.

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

- A Scope owns finalizers; brackets arrange acquisition, use, and release.
- In rat-stack, construct resources inside acquisition. The lint fence checks this shape.
- In rat-stack, [machines own finite business lifecycles](/lore/lifecycles-are-machines). Resource cleanup does not replace a lifecycle machine.

Start with [Layer composition](/lore/layers-make-dependencies-explicit) when choosing the resource's owner.
Then read [concurrent work](/lore/concurrent-work-needs-an-owner) before starting child work.
Return to the [Effect reading order](/lore/effect-basics#read-next).

## Sources

1. [Effect contributors. 2026. Effect. Effect 4.0.0.](https://github.com/Effect-TS/effect/blob/67ba4e46a11ccda0b6761578bfd22c04ae00167d/packages/effect/src/Effect.ts)
   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.](https://github.com/Effect-TS/effect/blob/67ba4e46a11ccda0b6761578bfd22c04ae00167d/ai-docs/src/01_effect/05_resources/10_acquire-release.ts)
   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.](https://github.com/joelhooks/rat-stack/blob/65e9465f38092e24486392f22c08f45d61230c20/packages/database/src/postgres-migrations.ts)
   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.](https://github.com/joelhooks/rat-stack/blob/4019cffbcbdb282d6f006854ff5b1f26d3b407e0/packages/core/test/scoped-resource.test.ts)
   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.](https://github.com/kitlangton/effect-solutions/blob/09f82e6c5c928e7232cd32daf04d7c6a830b63f7/packages/website/docs/08-testing.md)
   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.

## Lore on this page

- [The fence](/lore/the-fence)
- [Lifecycles are machines](/lore/lifecycles-are-machines)

## Linked from

- Agent guide → An Effect stack so pure (aspirational) Kit Langton will blush. → [Read page](https://ratstack.sh/llms.txt)

- Build and run rat-stack (README.md) → What is in the repo, how the example works, and how to run it. → [Read page](https://ratstack.sh/README.md)

- Change log → What changed in the files served here, newest first. → [Read page](https://ratstack.sh/log)

- Concurrent work needs an owner and a bound → Limit active work, retain result order, and stop child work when its owning lifetime ends. → [Read page](https://ratstack.sh/lore/concurrent-work-needs-an-owner)

- Effect + Alchemy peer patterns → Source-grounded patterns from repos on nearby Effect and Alchemy pins. → [Read page](https://ratstack.sh/resources/same-version-repos)

- Effect 4 study: September 2026 → Dated Effect 4 source studies from September 2026; current versions live in pins.md. → [Read page](https://ratstack.sh/resources/effect-4-reference-projects)

- Effect basics → An Effect describes work, expected failures, and required services before a runtime executes it. → [Read page](https://ratstack.sh/lore/effect-basics)

- Full agent guide → An Effect stack so pure (aspirational) Kit Langton will blush. → [Read page](https://ratstack.sh/llms-full.txt)

- Lifecycles are machines → XState owns finite lifecycle states while declared Effect actors own side effects. → [Read page](https://ratstack.sh/lore/lifecycles-are-machines)

- Oxlint rule limits → How the current lint rules draw their syntax boundaries. → [Read page](https://ratstack.sh/resources/lint-rule-limits)

- Purpose and boundaries of rat-stack (VISION.md) → What this starter is for and what a useful copy should keep. → [Read page](https://ratstack.sh/VISION.md)

- Rat Stack lore | rat-stack → Short, source-grounded notes on the ideas and decisions behind rat-stack. → [Read page](https://ratstack.sh/lore)

- Run Effect at the integration boundary → Build the runtime where foreign code enters, reuse its services, and dispose it when that integration ends. → [Read page](https://ratstack.sh/lore/run-effect-at-the-boundary)

- ship → Learn how to ship a change through merge, CI, release, stage deployment, rollout, verification, rollback and flags. → [Read page](https://ratstack.sh/skills/ship)

- Source change history (log.md) → The generated change log as Markdown. → [Read page](https://ratstack.sh/log.md)

## Unlinked mentions

- [Build and run rat-stack (README.md)](https://ratstack.sh/README.md) → A new scope changes line lengths, so run the formatter once after the rename:

- [Effect + Alchemy peer patterns](https://ratstack.sh/resources/same-version-repos) → Fence: scope clients to one invocation and use a real embedded database for migrations and query tests; don’t let Drizzle’s driver type become the cross-provider API.

- [Effect 4 study: September 2026](https://ratstack.sh/resources/effect-4-reference-projects) → sleep on the Effect Clock so TestClock drives after, declared Effect actions fork in the actor scope, and R is collected from declared actions and actors via RequirementsFrom.

- [ship](https://ratstack.sh/skills/ship) → Do not expand the approved scope to credentials, infrastructure, schema, provider writes or real-reader sends.¶
