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
- If the function throws synchronously, switch the assertion to t.throws(fn)
- Wrap the sync-throwing call so it returns a rejected promise, e.g. t.throwsAsync(async () => fn())
- 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
- Match the assertion variant to whether the code is sync (t.throws) or async (t.throwsAsync)
- Know your API: argument validation often throws before a promise exists
- Add a quick unit check that the function under test returns a promise
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
- (validateExpectations error for t.throwsAsync())
- The `any` property of the second argument to `${assertion}`
- The second argument to `${assertion}` contains unexpected pr
- message
- `t.like()` selector must not contain circular references
AI-assisted analysis of avajs/ava@bbfd946322 (2026-09-02).
Data as JSON: /api/errors/e32f3567300f3907.
Report an issue: GitHub.