denoland/deno · error · TypeError

before() requires a function

Error message

before() requires a function

What it means

The node:test context exposes t.before(fn) to register a hook that runs once before this test's subtests. It stores fn in an internal hooks array and requires it to be a function; anything else (including undefined or the result of calling a function) throws TypeError 'before() requires a function'.

Source

Thrown at ext/node/polyfills/testing.ts:1606

  // which are commonly not awaited (e.g. fastify's suites), still run to
  // completion before the parent test settles.
  async _drainSubtests() {
    if (this.#subtestPromises.length === 0) return;
    const promises = ArrayPrototypeSplice(
      this.#subtestPromises,
      0,
      this.#subtestPromises.length,
    );
    for (const p of new SafeArrayIterator(promises)) {
      try {
        await p;
      } catch { /* failures already reported through the subtest's own step */ }
    }
  }

  before(fn, _options) {
    if (typeof fn !== "function") {
      throw new TypeError("before() requires a function");
    }
    ArrayPrototypePush(this.#beforeHooks, fn);
  }

  after(fn, _options) {
    if (typeof fn !== "function") {
      throw new TypeError("after() requires a function");
    }
    ArrayPrototypePush(this.#afterHooks, fn);
  }

  beforeEach(fn, _options) {
    if (typeof fn !== "function") {
      throw new TypeError("beforeEach() requires a function");
    }
    ArrayPrototypePush(this.#beforeEachHooks, fn);
  }

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Pass the function reference: t.before(initDb)
  2. Keep argument order (fn, options) — options are the second parameter
  3. For async setup, pass the async function itself so the runner can await it

Example fix

// before
t.before({ timeout: 1000 }, connectDb);

// after
t.before(connectDb, { timeout: 1000 });
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof hook !== 'function') {
  throw new TypeError(`t.before: expected function, got ${typeof hook}`);
}
t.before(hook, { timeout: 1000 });

Type guard

const isHookFn = (fn) => typeof fn === 'function';

if (isHookFn(setup)) {
  t.before(setup);
}

Prevention

When it happens

Trigger: t.before() with no argument; t.before({ timeout: 1000 }, hook) with arguments in the wrong order (signature is (fn, options)); t.before(initDb()) passing a Promise instead of the function.

Common situations: Calling the setup function instead of referencing it; options-first habit carried over from other hook APIs; wiring hooks from config where the value can be undefined.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/964b316731e0b9c2. Report an issue: GitHub.