avajs/ava · error · AssertionError

{message}

Error message

{message}

What it means

In AVA, t.throwsAsync() expects a function that returns a promise (or a promise itself) which should reject. This AssertionError is thrown when the function throws synchronously instead of returning a rejecting promise. AVA detects the sync throw inside the try/catch around retval = thrower() and fails the assertion, suggesting you use t.throws() instead.

Source

Thrown at lib/assert.js:533

				pending(intermediate, assertionStack);
				return intermediate;
			};

			if (isPromise(thrower)) {
				return handlePromise(thrower, false);
			}

			let retval;
			let actual = null;
			try {
				retval = thrower();
			} catch (error) {
				actual = error;
			}

			if (actual) {
				throw fail(new AssertionError(message, {
					assertion: 't.throwsAsync()',
					cause: actual,
					formattedDetails: [formatWithLabel('Function threw synchronously. Use `t.throws()` instead:', actual)],
				}));
			}

			if (isPromise(retval)) {
				return handlePromise(retval, true);
			}

			throw fail(new AssertionError(message, {
				assertion: 't.throwsAsync()',
				formattedDetails: [formatWithLabel('Function returned:', retval)],
			}));
		});

		this.notThrows = withSkip((fn, message) => {
			assertMessage(message, 't.notThrows()');

View on GitHub (pinned to bbfd946322)

Solutions

  1. If the function throws synchronously, switch the assertion to t.throws(fn)
  2. Wrap the sync-throwing call so it returns a rejected promise, e.g. t.throwsAsync(async () => fn())
  3. Verify the function under test is genuinely async and returns a promise

Example fix

// before
t.throwsAsync(() => parseConfig(bad), {instanceOf: SyntaxError});
// after
// parseConfig throws synchronously, so use:
t.throws(() => parseConfig(bad), {instanceOf: SyntaxError});
Defensive patterns

Strategy: validation

Validate before calling

function isAsyncThrowingCandidate(fn) {
  return typeof fn === 'function';
}
// Prefer asserting sync throws directly:
// if (doesThrowSync(fn)) t.throws(fn); else await t.throwsAsync(fn);

Type guard

const returnsPromise = (value) =>
  typeof value === 'object' && value !== null && typeof value.then === 'function';

Try / catch

try {
  await t.throwsAsync(fn, {instanceOf: MyError});
} catch (e) {
  if (/threw synchronously/.test(e.message)) t.throws(fn);
  else throw e;
}

Prevention

When it happens

Trigger: Calling t.throwsAsync(() => { throw new Error('x'); }) where the callback throws before returning a promise; passing a sync-throwing wrapper around an async API; calling an async API that validates arguments synchronously and throws before creating its promise.

Common situations: Testing functions that are actually synchronous (misremembering which API is async); argument-validation in a function that throws before any promise is created; refactoring a sync function to async or vice versa and not updating the assertion.

Related errors


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