avajs/ava · error · TypeError

Expected a function

Error message

Expected a function

What it means

t.teardown(callback) registers a function to run when the test finishes. AVA requires a function; passing any non-callable value throws this TypeError immediately. Note the related guard: t.teardown() called during teardown records a different error instead.

Source

Thrown at lib/test.js:474

	clearTimeout() {
		nowAndTimers.clearTimeout(this.timeoutTimer);
		this.timeoutTimer = null;
	}

	addTeardown(callback) {
		if (this.isHook) {
			this.saveFirstError(new Error('`t.teardown()` is not allowed in hooks'));
			return;
		}

		if (this.finishing) {
			this.saveFirstError(new Error('`t.teardown()` cannot be used during teardown'));
			return;
		}

		if (typeof callback !== 'function') {
			throw new TypeError('Expected a function');
		}

		this.teardowns.push(callback);
	}

	async runTeardowns() {
		const teardowns = this.teardowns.toReversed();

		for (const teardown of teardowns) {
			try {
				await teardown(); // eslint-disable-line no-await-in-loop
			} catch (error) {
				this.saveFirstError(error);
			}
		}
	}

	verifyPlan() {

View on GitHub (pinned to bbfd946322)

Solutions

  1. Pass the function reference, don't call it: t.teardown(cleanup) not t.teardown(cleanup()).
  2. Verify the callback is defined at call time (imports, hoisting).
  3. Prefer t.after(fn) for hooks that can be async and receive context; keep t.teardown for plain functions.

Example fix

// before
t.teardown(stopServer()); // passes return value

// after
t.teardown(stopServer); // passes the function itself
Defensive patterns

Strategy: validation

Validate before calling

if (typeof callback !== 'function') throw new TypeError('t.teardown() expects a function, got: ' + typeof callback);
t.teardown(callback);

Type guard

const isCallable = (v) => typeof v === 'function';

Try / catch

try {
  t.teardown(callback);
} catch (err) {
  if (err instanceof TypeError && err.message === 'Expected a function') {
    throw new Error('t.teardown() called without a function — did you invoke the callback instead of passing it?');
  }
  throw err;
}

Prevention

When it happens

Trigger: t.teardown(myVar) where myVar is undefined (bad import); t.teardown(await cleanup()) which passes the result instead of the function; passing a promise or a boolean flag instead of a callback.

Common situations: Confusing t.teardown with t.after (t.after accepts hooks the same way, but the value passed may be the result of an eagerly-invoked function); calling cleanup() instead of passing cleanup; typos in import names.

Related errors


AI-assisted analysis of avajs/ava@bbfd946322 (2026-09-02). Data as JSON: /api/errors/5f3d82dbc8b1cccd. Report an issue: GitHub.