denoland/deno · error · TypeError

after() requires a function argument

Error message

after() requires a function argument

What it means

The module-level after(fn) from node:test registers a one-time teardown hook (attached to the current TAP suite when in TAP mode, otherwise to the root hooks). Like before(), it validates its first argument is a function and throws TypeError 'after() requires a function argument' for anything else, including a called function's return value.

Source

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

      ArrayPrototypePush(tapSuite.beforeAllHooks ??= [], fn);
      return;
    }
    ArrayPrototypePush(rootBeforeHooks, fn);
    // A bare top-level `before()` with no tests must still produce TAP
    // output (`before` runs, then `TAP version 13`, then `1..0`).
    scheduleTapRun();
    return;
  }
  if (currentSuite) {
    ArrayPrototypePush(currentSuite.beforeAllHooks, fn);
    return;
  }
  ArrayPrototypePush(rootBeforeHooks, fn);
}

function after(fn, _options) {
  if (typeof fn !== "function") {
    throw new TypeError("after() requires a function argument");
  }
  if (isTapMode()) {
    const tapSuite = getTapCurrentSuite();
    if (tapSuite !== null) {
      ArrayPrototypePush(tapSuite.afterAllHooks ??= [], fn);
      return;
    }
    ArrayPrototypePush(rootAfterHooks, fn);
    scheduleTapRun();
    return;
  }
  if (currentSuite) {
    ArrayPrototypePush(currentSuite.afterAllHooks, fn);
    return;
  }
  ArrayPrototypePush(rootAfterHooks, fn);
}

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Pass the function reference: after(closeDatabase)
  2. Guard optional hooks: if (teardown) after(teardown)
  3. Keep the (fn, options) argument order

Example fix

// before
after(closePool());

// after
after(closePool);
Defensive patterns

Strategy: type-guard

Validate before calling

import { after } from 'node:test';

if (typeof teardown !== 'function') {
  throw new TypeError(`after: expected function, got ${typeof teardown}`);
}
after(teardown);

Type guard

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

if (isHookFn(teardown)) {
  after(teardown);
}

Prevention

When it happens

Trigger: after() with no argument; after(teardown()) passing a Promise; passing an options object in the fn position.

Common situations: Cleanup helpers invoked instead of referenced during refactors; optional teardowns that are undefined behind feature flags.

Related errors


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