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
- Pass a thunk: await t.throwsAsync(() => asyncFn()).
- Pass the promise itself: await t.throwsAsync(somePromise).
- 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
- Wrap async calls in arrow functions: () => asyncFn(), never asyncFn().
- If the call throws synchronously, capture it with t.throws(() => ...) instead.
- Never pass Error instances or plain values as the thrower.
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
- `t.throws()` must be called with a function
- `t.like()` selector must not contain circular references
- (validateExpectations error for t.throws())
- (validateExpectations error for t.throwsAsync())
- `t.notThrows()` must be called with a function
AI-assisted analysis of avajs/ava@bbfd946322 (2026-09-02).
Data as JSON: /api/errors/6edbdf3f6396df15.
Report an issue: GitHub.