avajs/ava · error · AssertionError

`t.throwsAsync()` must be called with a function or promise

Error message

`t.throwsAsync()` must be called with a function or promise

What it means

AVA throws this fixed usage error when t.throwsAsync() is given a first argument that is neither a function nor a promise. t.throwsAsync() must observe a rejection from one of those two shapes; anything else (undefined, a value, an Error instance) cannot reject, so the library flags improper usage with 'Called with:' detail.

Source

Thrown at lib/assert.js:474

				throw fail(error);
			}
		});

		this.throwsAsync = withSkip(async (...args) => {
			let [thrower, expectations, message] = args;

			try {
				assertMessage(message, 't.throwsAsync()');
			} catch (error) {
				try {
					await thrower;
				} catch {}

				throw error;
			}

			if (typeof thrower !== 'function' && !isPromise(thrower)) {
				throw fail(new AssertionError('`t.throwsAsync()` must be called with a function or promise', {
					assertion: 't.throwsAsync()',
					formattedDetails: [formatWithLabel('Called with:', thrower)],
				}));
			}

			try {
				expectations = validateExpectations('t.throwsAsync()', expectations, args.length, experiments);
			} catch (error) {
				try {
					await thrower;
				} catch {}

				throw fail(error);
			}

			const handlePromise = async (promise, wasReturned) => {
				// Record the stack before it gets lost in the promise chain.
				const assertionStack = getAssertionStack();

View on GitHub (pinned to bbfd946322)

Solutions

  1. Pass a thunk: await t.throwsAsync(() => asyncFn()).
  2. Pass the promise itself: await t.throwsAsync(somePromise).
  3. If the call throws synchronously, use t.throws(() => ...) instead.

Example fix

// before
await t.throwsAsync(asyncFn()); // throws synchronously, returns nothing
// after
await t.throwsAsync(() => asyncFn());
Defensive patterns

Strategy: type-guard

Validate before calling

const throwerVal = typeof thrower === 'function' ? thrower : thrower;
if (typeof throwerVal !== 'function' && !(throwerVal && typeof throwerVal.then === 'function')) {
  throw new TypeError('t.throwsAsync expects a function or promise; got ' + typeof throwerVal);
}

Type guard

const isValidThrower = (v) => typeof v === 'function' || (v != null && typeof v.then === 'function');

Try / catch

try {
  await t.throwsAsync(thrower);
} catch (err) {
  if (/must be called with a function or promise/.test(err.message)) {
    t.fail('Pass a thunk or a promise: t.throwsAsync(() => asyncFn())');
  } else { throw err; }
}

Prevention

When it happens

Trigger: await t.throwsAsync(doAsync()) where doAsync throws synchronously before returning a promise; t.throwsAsync(await somePromise); t.throwsAsync() with no arguments; passing a rejected Error value directly.

Common situations: Calling an async function that throws before its first await so no promise is produced; forgetting to await/pass the promise; wrapping the wrong expression in the assertion.

Related errors


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