avajs/ava · error · AssertionError

`t.notThrowsAsync()` must be called with a function or promi

Error message

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

What it means

t.notThrowsAsync() asserts that a function returning a promise (or a promise itself) does not reject. AVA validates its argument up front and throws this AssertionError when it is neither a function nor a promise-like object. Like other improperUsage errors, it signals a mistake in how the assertion was invoked.

Source

Thrown at lib/assert.js:586

				}));
			}

			return pass();
		});

		this.notThrowsAsync = withSkip(async (nonThrower, message) => {
			try {
				assertMessage(message, 't.notThrowsAsync()');
			} catch (error) {
				try {
					await nonThrower;
				} catch {}

				throw error;
			}

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

			const handlePromise = async (promise, wasReturned) => {
				// Create an error object to record the stack before it gets lost in the promise chain.
				const assertionStack = getAssertionStack();
				// Handle "promise like" objects by casting to a real Promise.
				const intermediate = Promise.resolve(promise).then(noop, error => {
					throw failPending(new AssertionError(message, {
						assertion: 't.notThrowsAsync()',
						assertionStack,
						formattedDetails: [formatWithLabel(`${wasReturned ? 'Returned promise' : 'Promise'} rejected with:`, error)],
					}));
				});
				pending(intermediate, assertionStack);

View on GitHub (pinned to bbfd946322)

Solutions

  1. Pass the promise directly: t.notThrowsAsync(asyncFn()) without await
  2. Or pass a function returning a promise: t.notThrowsAsync(() => asyncFn())
  3. If the code is synchronous, use t.notThrows(() => syncFn()) instead

Example fix

// before
await t.notThrowsAsync(await fetchUser(id));
// after
await t.notThrowsAsync(fetchUser(id));
Defensive patterns

Strategy: type-guard

Validate before calling

const arg = asyncFn();
if (typeof arg !== 'function' && !(arg && typeof arg.then === 'function')) {
  throw new TypeError('notThrowsAsync needs a function or a promise — remove any await');
}

Type guard

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

Try / catch

try {
  await t.notThrowsAsync(promiseOrFn);
} catch (e) {
  if (/must be called with a function or promise/.test(e.message)) {
    throw new TypeError('Do not await before passing: t.notThrowsAsync(asyncFn())');
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing an awaited value (the resolved result) instead of the promise; passing a plain value, number, or string; passing a function to t.notThrowsAsync that is meant for t.notThrows without returning a promise while also failing the type check because a non-callable was given.

Common situations: Writing t.notThrowsAsync(await asyncFn()) — the await removes the promise; confusing t.notThrows and t.notThrowsAsync during refactors; passing null/undefined due to an earlier failed lookup.

Related errors


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