jestjs/jest · error · JestAssertionError
received value must be a promise or a function returning a…
Error message
received value must be a promise or a function returning a promise
What it means
expect(actual).resolves.<matcher>() requires `actual` to be a Promise (or a function returning one) so it can await it before running the matcher. makeResolveMatcher throws a JestAssertionError if, after unwrapping, the value is not a promise. This catches the common mistake of chaining .resolves onto a synchronous value.
Solutions
- Ensure the value passed to expect() is a Promise: `expect(asyncFn()).resolves.toBe(x)` — call the function so it returns a promise.
- Drop `.resolves` and assert directly if the value is synchronous: `expect(syncFn()).toBe(x)`.
- If passing a function, make sure it returns a Promise: `expect(() => fetch(url)).resolves.toBe(...)`.
Example fix
// before expect(getValue).resolves.toBe(42); // getValue is sync, not a promise/function-returning-promise // after (if getValue is async) expect(getValue()).resolves.toBe(42); // after (if getValue is sync) expect(getValue()).toBe(42);
Defensive patterns
Strategy: type-guard
Validate before calling
function isThenable(v) {
return v != null && (typeof v === 'object' || typeof v === 'function') && typeof v.then === 'function';
}
// Only chain .resolves when actually thenable
const probe = typeof actual === 'function' ? actual() : actual;
if (!isThenable(probe)) {
throw new TypeError('value is not a promise; remove .resolves');
} Type guard
function isPromise<T>(v: unknown): v is Promise<T> {
return !!v && typeof (v as any).then === 'function';
} Prevention
- Call async functions before expect so a promise is passed: expect(asyncFn()).resolves....
- Drop .resolves for synchronous values.
- Prefer await expect(...) inside async tests for clarity where supported.
When it happens
Trigger: Writing `expect(value).resolves.toBe(x)` where value is not a promise (e.g. a plain number, object, or a function that does not return a promise). The check runs before any matcher dispatch on the .resolves branch.
Common situations: Forgetting to await the function call whose result is asserted; testing a sync function with .resolves; a function whose async branch was not taken so it returned undefined; refactoring an async function to sync without updating tests.
Related errors
- Expect takes at most one argument.
- Jest: concurrent test
- any() expects to be passed a constructor function. Please…
- describe does not expect any arguments
- expect.customEqualityTesters: Must be set to an array of…
AI-assisted analysis of jestjs/jest@8e6d128e4a (2026-08-10).
Data as JSON: /api/errors/852eef1578891dc3.
Report an issue: GitHub.
Appendix: source
Thrown at packages/expect/src/index.ts:181
const makeResolveMatcher =
(
matcherName: string,
matcher: RawMatcherFn,
isNot: boolean,
actual: Promise<any> | (() => Promise<any>),
outerErr: JestAssertionError,
): PromiseMatcherFn =>
(...args) => {
const options = {
isNot,
promise: 'resolves',
};
const actualWrapper: Promise<any> =
typeof actual === 'function' ? actual() : actual;
if (!isPromise(actualWrapper)) {
throw new JestAssertionError(
matcherUtils.matcherErrorMessage(
matcherUtils.matcherHint(matcherName, undefined, '', options),
`${matcherUtils.RECEIVED_COLOR(
'received',
)} value must be a promise or a function returning a promise`,
matcherUtils.printWithType(
'Received',
actual,
matcherUtils.printReceived,
),
),
);
}
const innerErr = new JestAssertionError();
return actualWrapper.then(
result =>View on GitHub (pinned to 8e6d128e4a)