vitest-dev/vitest · error · AssertionError
expected promise to throw an error, but it didn't
Error message
expected promise to throw an error, but it didn't
What it means
Thrown in the toThrow/throws/Throw overwrite when the assertion is in 'resolves' mode (expect(p).resolves.toThrow()), the awaited value is not a function, and the assertion is not negated. It means: the promise resolved successfully to a non-throwing value, so there was nothing to throw.
Solutions
- Use await expect(promise).rejects.toThrow() when you expect the promise itself to reject.
- If the promise resolves to a function and you want that function's throw, ensure the resolved value is actually a function.
- Re-check the assertion intent — most often .rejects is what was meant.
Example fix
// before
await expect(fetchUser()).resolves.toThrow()
// after
await expect(fetchUser()).rejects.toThrow('not found') Defensive patterns
Strategy: validation
Validate before calling
// Clarify intent before writing the assertion.
function expectRejection(promise) {
return expect(promise).rejects
}
// then: await expectRejection(fetchUser()).toThrow('not found') Try / catch
try {
await expect(promise).resolves.toThrow()
} catch (e) {
if (/expected promise to throw/.test(String(e?.message))) {
// likely meant .rejects — retry against rejection
await expect(promise).rejects.toThrow()
return
}
throw e
} Prevention
- Use .rejects.toThrow() to assert a promise rejects; .resolves.toThrow() only fits promises that resolve to a throwing function.
- Double-check the resolved value type before chaining .resolves.toThrow.
- Prefer await expect(...).rejects to await expect(...).resolves.toThrow unless you intentionally return a function.
When it happens
Trigger: await expect(promise).resolves.toThrow() where the promise resolves to a plain value (not a function) and .not is not present. The resolves branch expects a function that throws when called; a resolved value has nothing to invoke.
Common situations: Confusing resolves.toThrow with rejects.toThrow — wanting the promise to reject, not the resolved value to throw; calling toThrow on a promise that resolves to data expecting it to validate errors.
Related errors
- Async schema validation is not supported in asymmetric…
- Exact option does not support RegExp expected class names
- expect.poll() is not supported in combination with .
- toContain() expected a DOM node as the argument, but got
- .toContainHTML() expects a string value, got
AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11).
Data as JSON: /api/errors/e66aa44ae3212e36.
Report an issue: GitHub.
Appendix: source
Thrown at packages/expect/src/jest-expect.ts:90
const object = utils.flag(this, 'object')
const isNot = utils.flag(this, 'negate') as boolean
if (promise === 'rejects') {
utils.flag(this, 'object', () => {
throw object
})
}
// if it got here, it's already resolved
// unless it tries to resolve to a function that should throw
// called as '.resolves[.not].toThrow()`
else if (promise === 'resolves' && typeof object !== 'function') {
if (!isNot) {
const message
= utils.flag(this, 'message')
|| 'expected promise to throw an error, but it didn\'t'
const error = {
showDiff: false,
}
throw new AssertionError(message, error, utils.flag(this, 'ssfi'))
}
else {
return
}
}
_super.apply(this, args)
}
})
})
// @ts-expect-error @internal
def('withTest', function (test: Test) {
utils.flag(this, 'vitest-test', test)
return this
})
def('toEqual', function (expected) {
const actual = utils.flag(this, 'object')View on GitHub (pinned to 1fa9837ec2)