{"id":"94da80e25a828d50","repo":"jestjs/jest","slug":"resolver-located-at-resolver-does-not-export-a","errorCode":null,"errorMessage":"Resolver located at ${resolver} does not export a function or an object with \"sync\" and \"async\" props","messagePattern":"Resolver located at (.+?) does not export a function or an object with \"sync\" and \"async\" props","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/jest-resolve/src/resolver.ts","lineNumber":959,"sourceCode":"\n  const loadedResolver = require(resolver);\n\n  if (loadedResolver == null) {\n    throw new Error(`Resolver located at ${resolver} does not export anything`);\n  }\n\n  if (typeof loadedResolver === 'function') {\n    return loadedResolver as SyncResolver;\n  }\n\n  if (\n    typeof loadedResolver === 'object' &&\n    (loadedResolver.sync != null || loadedResolver.async != null)\n  ) {\n    return loadedResolver as ResolverObject;\n  }\n\n  throw new Error(\n    `Resolver located at ${resolver} does not export a function or an object with \"sync\" and \"async\" props`,\n  );\n}\n","sourceCodeStart":941,"sourceCodeEnd":963,"githubUrl":"https://github.com/jestjs/jest/blob/f49721c78e195558b40913977c9230f5b7f559d8/packages/jest-resolve/src/resolver.ts#L941-L963","documentation":"Thrown inside loadResolver when the resolver module exports a value that is neither a function nor an object exposing a `sync` or `async` property. Jest requires one of these two shapes for a custom resolver and rejects anything else (e.g. a string, number, array, or object without the hooks).","triggerScenarios":"The `resolver` config value points at a module that exports a plain config object, a string, or an object missing both `sync` and `async` keys. Common when someone mistakes the resolver file for a config file or exports a wrapper object like `{ resolve: fn }`.","commonSituations":"Copy-pasting a resolver example but naming the export `resolve` instead of `sync`/`async`; exporting a class instance whose prototype methods aren't own enumerable `sync`/`async`; pointing `resolver` at the package root whose `main` is a JSON manifest.","solutions":["Export a function directly: `module.exports = function(path, options) { ... }`.","Or export `{ sync, async }` (named exactly those keys) where each is a function.","Verify with `node -e \"const r = require('./my-resolver.js'); console.log(typeof r, typeof r.sync, typeof r.async)\"` — at least one must be 'function'."],"exampleFix":"// before\nmodule.exports = { resolve: (path, options) => { /* ... */ } };\n\n// after\nmodule.exports = { sync: (path, options) => { /* ... */ } };","handlingStrategy":"type-guard","validationCode":"function assertResolverShape(resolverPath) {\n  const m = require(resolverPath);\n  const ok = typeof m === 'function' ||\n    (typeof m === 'object' && m && (typeof m.sync === 'function' || typeof m.async === 'function'));\n  if (!ok) throw new Error(`Resolver ${resolverPath} must export a function or {sync, async}`);\n  return m;\n}","typeGuard":"type ResolverExport = ((path: string, options: any) => string) | { sync?: Function; async?: Function };\nfunction isResolverExport(m: unknown): m is ResolverExport {\n  if (typeof m === 'function') return true;\n  if (typeof m !== 'object' || m === null) return false;\n  return typeof (m as any).sync === 'function' || typeof (m as any).async === 'function';\n}","tryCatchPattern":null,"preventionTips":["Name the exported hooks exactly `sync` and `async` — no other keys are recognized.","For a single-implementation resolver, export the function directly to avoid shape mistakes.","Document the expected export shape in the resolver file's header comment."],"tags":["resolver","config","exports","validation"],"analyzedSha":"f49721c78e195558b40913977c9230f5b7f559d8","analyzedAt":"2026-08-03T20:16:28.571Z","schemaVersion":2}