{"record":{"id":"60caa7fcd816f695","repo":"jestjs/jest","slug":"a-describe-callback-must-not-return-a-value","errorCode":null,"errorMessage":"A \"describe\" callback must not return a value.","messagePattern":"A \"describe\" callback must not return a value\\.","errorType":"exception","errorClass":"ErrorWithStack","httpStatus":null,"severity":"error","filePath":"packages/jest-circus/src/index.ts","lineNumber":84,"sourceCode":"    asyncError.message = (error as Error).message;\n    throw asyncError;\n  }\n\n  dispatchSync({\n    asyncError,\n    blockName,\n    mode,\n    name: 'start_describe_definition',\n  });\n  const describeReturn = blockFn();\n\n  if (isPromise(describeReturn)) {\n    throw new ErrorWithStack(\n      'Returning a Promise from \"describe\" is not supported. Tests must be defined synchronously.',\n      describeFn,\n    );\n  } else if (describeReturn !== undefined) {\n    throw new ErrorWithStack(\n      'A \"describe\" callback must not return a value.',\n      describeFn,\n    );\n  }\n\n  dispatchSync({blockName, mode, name: 'finish_describe_definition'});\n};\n\nconst _addHook = (\n  fn: Circus.HookFn,\n  hookType: Circus.HookType,\n  hookFn: THook,\n  timeout?: number,\n) => {\n  const asyncError = new ErrorWithStack(undefined, hookFn);\n\n  if (typeof fn !== 'function') {\n    asyncError.message =","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/jestjs/jest/blob/8e6d128e4a278059ecddecaa97400b04c8ae5fd9/packages/jest-circus/src/index.ts#L66-L102","documentation":"After invoking the describe callback, _dispatchDescribe also rejects any non-undefined, non-Promise return value (index.ts:83-87). The callback is meant only to register tests and hooks as a side effect; returning a value is almost always a typo (an accidental arrow-function shorthand return) that would silently lose the intended side effect.","triggerScenarios":"Writing describe('x', () => test('y', () => {})) where the arrow implicitly returns the result of test(); any describe callback using the concise arrow body form that evaluates an expression; describe.each with a callback that returns a value.","commonSituations":"Accidentally dropping the braces on an arrow function inside describe so it returns test(...) instead of calling it as a statement; refactoring a callback from braces to expression form; copy-paste from a function that returns a value.","solutions":["Wrap the callback body in curly braces so calls are statements, not the return value (describe('x', () => { test(...); }).","Audit the describe callback for any return/implicit-return of an expression and convert to void or statement form.","If you genuinely need the value, move it into a hook or a variable assignment."],"exampleFix":"// before\ndescribe('math', () =>\n  test('adds', () => expect(1 + 1).toBe(2)));\n// after\ndescribe('math', () => {\n  test('adds', () => expect(1 + 1).toBe(2));\n});","handlingStrategy":"validation","validationCode":"// Reject describe callbacks whose return type is non-void statically (TS):\n// type SyncDescribeFn = () => void;\nfunction registerDescribe(name: string, fn: () => void) {\n  describe(name, fn);\n}\nregisterDescribe('x', () => { test('y', () => {}); }); // OK\n// registerDescribe('x', () => test('y', () => {})); // TS error: not assignable to () => void","typeGuard":"function returnsVoid(fn: (...args: any[]) => unknown): boolean {\n  // static check via TS is preferred; runtime heuristic for audits:\n  return fn.constructor.name !== 'AsyncFunction';\n}","tryCatchPattern":null,"preventionTips":["Type describe callbacks explicitly as () => void so implicit returns become TS errors.","Always wrap describe callback bodies in curly braces.","Run tsc --noImplicitReturns or enable the corresponding lint to catch implicit returns."],"tags":["jest-circus","describe","return-value","arrow-function"],"backgroundTag":null,"analyzedSha":"8e6d128e4a278059ecddecaa97400b04c8ae5fd9","analyzedAt":"2026-08-10T18:11:27.960Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}