jestjs/jest · error · Error

Unable to load resolver at ${options.resolver}

Error message

Unable to load resolver at ${options.resolver}

What it means

Thrown by Resolver.findNodeModuleAsync when the configured custom resolver module loads successfully but neither its `async` nor `sync` property resolves to a usable function at call time. It is a defensive guard inside the async resolution path that protects against a resolver object whose exported hooks are missing or falsy after the looser shape-check in loadResolver passed.

Source

Thrown at packages/jest-resolve/src/resolver.ts:159

  }

  static async findNodeModuleAsync(
    path: string,
    options: FindNodeModuleConfig,
  ): Promise<string | null> {
    const resolverModule = loadResolver(options.resolver);
    let resolver: ResolverInterface = defaultAsyncResolver;

    if (typeof resolverModule === 'function') {
      resolver = resolverModule;
    } else if (
      typeof resolverModule.async === 'function' ||
      typeof resolverModule.sync === 'function'
    ) {
      const asyncOrSync = resolverModule.async || resolverModule.sync;

      if (asyncOrSync == null) {
        throw new Error(`Unable to load resolver at ${options.resolver}`);
      }

      resolver = asyncOrSync;
    }

    const paths = options.paths;

    try {
      const result = await resolver(path, {
        basedir: options.basedir,
        conditions: options.conditions,
        defaultAsyncResolver,
        defaultResolver,
        extensions: options.extensions,
        moduleDirectory: options.moduleDirectory,
        paths: paths ? [...(nodePaths || []), ...paths] : nodePaths,
        rootDir: options.rootDir,
      });

View on GitHub (pinned to f49721c78e)

Solutions

  1. Open the file pointed to by `options.resolver` and confirm it exports either a function or an object with at least one of `sync`/`async` as a function.
  2. If you only need sync resolution, export the function directly: `module.exports = (path, options) => resolvedPath`.
  3. If you need both, export `{ sync, async }` where both are functions (or at least `async` is defined for the async path).
  4. Run `node -e "console.log(require('<resolver-path>'))"` to print the actual export shape the test process sees.

Example fix

// before (resolver.js)
module.exports = { sync: null, async: undefined };

// after
const { defaultResolver, defaultAsyncResolver } = require('jest-resolve/build/defaultResolver');
module.exports = {
  sync: (path, options) => defaultResolver(path, options),
  async: async (path, options) => defaultAsyncResolver(path, options),
};
Defensive patterns

Strategy: validation

Validate before calling

// Run before configuring Jest, or in a setup script
const fs = require('fs');
function validateResolver(resolverPath) {
  const mod = require(resolverPath);
  const isFn = typeof mod === 'function';
  const hasHook = mod && (typeof mod.sync === 'function' || typeof mod.async === 'function');
  if (!isFn && !hasHook) {
    throw new Error(`Resolver at ${resolverPath} must export a function or {sync, async}`);
  }
  return mod;
}

Type guard

// TypeScript
import type { SyncResolver, ResolverObject } from 'jest-resolve';
function isResolverExport(m: unknown): m is SyncResolver | ResolverObject {
  if (typeof m === 'function') return true;
  return typeof m === 'object' && m !== null &&
    (typeof (m as any).sync === 'function' || typeof (m as any).async === 'function');
}

Prevention

When it happens

Trigger: Setting `resolver` in jest config to a module that exports an object (e.g. `module.exports = { async: null }`) or whose `async`/`sync` properties are non-null but not callable, then running an async module resolution (ESM import or require(esm)). Also reachable if a transpiled/bundled resolver loses its function exports at runtime.

Common situations: Migrating from a sync-only custom resolver to the `{sync, async}` form and forgetting to wire the async hook; pointing `resolver` at a TypeScript source file whose compiled output exports the functions under different names; a resolver package that conditionally exports based on `require.main` or Node version and exports an empty object in the test process.

Related errors


AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03). Data as JSON: /data/errors/c497f09734c88bf4.json. Report an issue: GitHub.