Skip to content

@effect/vitest does not await finalizers when an it.effect test times out #8151

Description

@B4rz99

What version of Effect is running?

effect@4.0.0-rc.112
@effect/vitest@4.0.0-rc.112
vitest@4.1.11
Bun 1.4.1

What steps can reproduce the bug?

Create lifecycle.test.ts:

import { it } from "@effect/vitest"
import { Effect } from "effect"
import { expect } from "vitest"

const events: Array<string> = []

const resource = Effect.acquireRelease(
  Effect.sync(() => {
    events.push("acquired")
  }),
  () =>
    Effect.gen(function*() {
      events.push("release:start")
      yield* Effect.promise(
        () => new Promise((resolve) => setTimeout(resolve, 250))
      )
      events.push("release:end")
    })
)

it.effect(
  "times out",
  () =>
    Effect.gen(function*() {
      yield* resource
      return yield* Effect.never
    }),
  { timeout: 75 }
)

it.effect("runs after the timed-out test", () =>
  Effect.sync(() => {
    events.push("next:start")
    expect(events).toContain("release:end")
  })
)

Run it sequentially:

bunx vitest run lifecycle.test.ts --maxWorkers=1

The second test fails because Vitest starts it after the finalizer begins but before the finalizer completes.

The finalizer deliberately uses a real delayed Promise instead of Effect.sleep, because @effect/vitest supplies TestClock.

What is the expected behavior?

When Vitest times out an it.effect test, the Effect fiber should be interrupted and its finalizers should finish before Vitest advances to the next test.

The observed event order should be:

acquired
release:start
release:end
next:start

What do you see instead?

The second test fails with:

actual: [ 'acquired', 'release:start', 'next:start' ]

release:end has not occurred before Vitest advances.

Finalizers do finish before Vitest advances when the Effect completes through ordinary success, typed failure, defect, or assertion failure. The problem is specific to the timeout path in this reproduction.

Additional information

Programmatic run cancellation appears to have the same lifecycle behavior: the Effect finalizer starts, but Vitest shutdown can complete before the finalizer finishes. I can provide a separate reproduction for cancellation if that should be tracked independently.

I searched the existing Effect and Vitest issues and did not find one covering this exact finalizer-completion behavior.

A potentially relevant implementation reference is Effect PR #8135: its Rstest adapter explicitly awaits the interrupted fiber after a test timeout.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions