denoland/deno · error · TypeError

afterEach() requires a function

Error message

afterEach() requires a function

What it means

t.afterEach(fn) on the node:test context registers a hook run after each of this test's subtests. As with the sibling hooks, only a function argument is accepted; anything else throws TypeError 'afterEach() requires a function'.

Source

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

  }

  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);
  }

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

  // Runs this context's `before()` hooks a single time, before its first
  // subtest. Idempotent so it is safe to call from every subtest's step.
  async _runBeforeHooksOnce() {
    if (this.#beforeHooksRun) return;
    this.#beforeHooksRun = true;
    // deno-lint-ignore no-this-alias
    const ctx = this;
    for (const hook of new SafeArrayIterator(this.#beforeHooks)) {
      await runInTestContext(ctx, () => hook(ctx));
    }
  }

  // Runs this context's `after()` hooks a single time, after its body and all
  // of its subtests have completed. Idempotent so success and failure paths can

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Pass the function reference: t.afterEach(truncateTable)
  2. Guard optional hooks: if (cleanup) t.afterEach(cleanup)
  3. Keep the (fn, options) argument order

Example fix

// before
t.afterEach(cleanUp());

// after
t.afterEach(cleanUp);
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof cleanup !== 'function') {
  throw new TypeError(`t.afterEach: expected function, got ${typeof cleanup}`);
}
t.afterEach(cleanup);

Type guard

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

if (isHookFn(cleanup)) {
  t.afterEach(cleanup);
}

Prevention

When it happens

Trigger: t.afterEach() with no argument; t.afterEach(truncateTable()) passing a Promise; passing a config object in the fn position.

Common situations: Cleanup helpers called instead of referenced; optional cleanup that is undefined when a feature flag is off.

Related errors


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