{"id":"852eef1578891dc3","repo":"jestjs/jest","slug":"received-value-must-be-a-promise-or-a-function-ret","errorCode":null,"errorMessage":"received value must be a promise or a function returning a promise","messagePattern":"received value must be a promise or a function returning a promise","errorType":"exception","errorClass":"JestAssertionError","httpStatus":null,"severity":"error","filePath":"packages/expect/src/index.ts","lineNumber":181,"sourceCode":"const makeResolveMatcher =\n  (\n    matcherName: string,\n    matcher: RawMatcherFn,\n    isNot: boolean,\n    actual: Promise<any> | (() => Promise<any>),\n    outerErr: JestAssertionError,\n  ): PromiseMatcherFn =>\n  (...args) => {\n    const options = {\n      isNot,\n      promise: 'resolves',\n    };\n\n    const actualWrapper: Promise<any> =\n      typeof actual === 'function' ? actual() : actual;\n\n    if (!isPromise(actualWrapper)) {\n      throw new JestAssertionError(\n        matcherUtils.matcherErrorMessage(\n          matcherUtils.matcherHint(matcherName, undefined, '', options),\n          `${matcherUtils.RECEIVED_COLOR(\n            'received',\n          )} value must be a promise or a function returning a promise`,\n          matcherUtils.printWithType(\n            'Received',\n            actual,\n            matcherUtils.printReceived,\n          ),\n        ),\n      );\n    }\n\n    const innerErr = new JestAssertionError();\n\n    return actualWrapper.then(\n      result =>","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/jestjs/jest/blob/f49721c78e195558b40913977c9230f5b7f559d8/packages/expect/src/index.ts#L163-L199","documentation":"expect(x).resolves.<matcher>() wraps the actual value (or calls it if a function) and checks isPromise. If the value is not a Promise (and calling it didn't return one), it throws a JestAssertionError with a matcherHint header. .resolves only makes sense for promises; using it on a sync value is a misuse the library refuses to silently await.","triggerScenarios":"Calling expect(5).resolves.toBe(5), expect('foo').resolves.toEqual('foo'), expect(() => 42).resolves.toBe(42) (function returns a non-promise), or expect(maybePromise).resolves... where maybePromise resolved to a non-promise value.","commonSituations":"Forgetting to await the function before expect; testing a sync function with .resolves; refactoring an async function to sync without removing .resolves; double-awaiting.","solutions":["Use plain expect for synchronous values: expect(value).toBe(5).","Ensure the value is actually a Promise: expect(asyncFn()).resolves.toBe(5) — call the async function so it returns a promise.","Add `await` where needed so you pass the unwrapped value to a sync matcher, or keep .resolves and pass the promise itself."],"exampleFix":"// before\nfunction getAnswer() { return 42; }\nexpect(getAnswer()).resolves.toBe(42); // not a promise\n\n// after — sync matcher\nexpect(getAnswer()).toBe(42);\n// or, if it should be async:\nasync function getAnswer() { return 42; }\nexpect(getAnswer()).resolves.toBe(42);","handlingStrategy":"type-guard","validationCode":"const value = typeof actual === 'function' ? actual() : actual;\nif (!isPromise(value)) {\n  throw new TypeError('use plain expect() for non-promise values');\n}\n// now safe to use .resolves","typeGuard":"function isThenable(x: unknown): x is Promise<unknown> {\n  return !!x && typeof (x as any).then === 'function';\n}","tryCatchPattern":"// .resolves throws synchronously before returning the chained matcher — convert to a sync matcher or fix the source","preventionTips":["Use .resolves only on values that are actually promises.","For sync values use plain expect(actual).toBe(...).","Double-check that async functions are invoked so they return a promise."],"tags":["expect","resolves","async","promise","api-misuse"],"analyzedSha":"f49721c78e195558b40913977c9230f5b7f559d8","analyzedAt":"2026-08-03T20:16:28.571Z","schemaVersion":2}