{"record":{"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/8e6d128e4a278059ecddecaa97400b04c8ae5fd9/packages/expect/src/index.ts#L163-L199","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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(...)`."],"exampleFix":"// before\nexpect(getValue).resolves.toBe(42); // getValue is sync, not a promise/function-returning-promise\n\n// after (if getValue is async)\nexpect(getValue()).resolves.toBe(42);\n// after (if getValue is sync)\nexpect(getValue()).toBe(42);","handlingStrategy":"type-guard","validationCode":"function isThenable(v) {\n  return v != null && (typeof v === 'object' || typeof v === 'function') && typeof v.then === 'function';\n}\n\n// Only chain .resolves when actually thenable\nconst probe = typeof actual === 'function' ? actual() : actual;\nif (!isThenable(probe)) {\n  throw new TypeError('value is not a promise; remove .resolves');\n}","typeGuard":"function isPromise<T>(v: unknown): v is Promise<T> {\n  return !!v && typeof (v as any).then === 'function';\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["expect","resolves","async","promise","assertion"],"backgroundTag":null,"analyzedSha":"8e6d128e4a278059ecddecaa97400b04c8ae5fd9","analyzedAt":"2026-08-10T18:11:27.960Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}