jestjs/jest · error · TypeError

Cannot merge config in form of callback

Error message

Cannot merge config in form of callback

What it means

mergeConfig (index.ts:66-78) deep-merges two plain config objects. It inspects their properties directly, so it cannot operate on the function/async-function config form that defineConfig supports. The runtime guard at index.ts:73-75 throws a TypeError if either argument is callable, matching the documented limitation that mergeConfig only accepts object configs.

Solutions

  1. Resolve the function to an object first: const base = await baseConfig(); then mergeConfig(base, overrides).
  2. Restructure so both arguments to mergeConfig are plain objects (compute dynamic values before merging).
  3. For env-dependent overrides, apply them inside a single callback config rather than merging two callbacks.

Example fix

// before
const base = () => ({ testEnvironment: 'node' });
mergeConfig(base, { verbose: true });
// after
const base = { testEnvironment: 'node' };
mergeConfig(base, { verbose: true });
// or resolve first:
const base = await baseFn();
mergeConfig(base, { verbose: true });
Defensive patterns

Strategy: type-guard

Validate before calling

function safeMergeConfig<D extends object, O extends object>(d: D | (() => D), o: O | (() => O)) {
  if (typeof d === 'function' || typeof o === 'function') {
    throw new TypeError('Cannot mergeConfig function-form configs; resolve them to objects first');
  }
  return mergeConfig(d, o);
}

Type guard

function isPlainConfigObject<T extends object>(c: unknown | (() => T) | (() => Promise<T>)): c is T {
  return typeof c === 'object' && c !== null && typeof (c as any) !== 'function';
}

Prevention

When it happens

Trigger: Calling mergeConfig(() => ({...}), {...}); mergeConfig({...}, async () => ({...})); passing a jest.config.ts that exports a function into mergeConfig; chaining defineConfig(fn) outputs into mergeConfig.

Common situations: Writing a base/override config split where one side is a function for env-based config; refactoring a config from object to callback form without updating the merge call; trying to merge presets defined as functions.

Related errors


AI-assisted analysis of jestjs/jest@8e6d128e4a (2026-08-10). Data as JSON: /api/errors/4af9439269eb5bb4. Report an issue: GitHub.

Appendix: source

Thrown at packages/jest-config/src/index.ts:74

export function defineConfig(config: UserConfigFnObject): UserConfigFnObject;
export function defineConfig(config: UserConfigFnPromise): UserConfigFnPromise;
export function defineConfig(config: UserConfigFn): UserConfigFn;
export function defineConfig(config: UserConfigExport): UserConfigExport {
  return config;
}

/**
 * Merges two configuration objects, where the second object takes precedence over the first one.
 */
export function mergeConfig<
  D extends UserConfigExport,
  O extends UserConfigExport,
>(
  defaults: D extends Function ? never : D,
  overrides: O extends Function ? never : O,
): JestTestConfigObject {
  if (typeof defaults === 'function' || typeof overrides === 'function') {
    throw new TypeError('Cannot merge config in form of callback');
  }

  return deepMerge.all([defaults, overrides]);
}

export async function readConfig(
  argv: Config.Argv,
  packageRootOrConfig: string | Config.InitialOptions,
  // Whether it needs to look into `--config` arg passed to CLI.
  // It only used to read initial config. If the initial config contains
  // `project` property, we don't want to read `--config` value and rather
  // read individual configs for every project.
  skipArgvConfigOption?: boolean,
  parentConfigDirname?: string | null,
  projectIndex = Number.POSITIVE_INFINITY,
  skipMultipleConfigError = false,
): Promise<ReadConfig> {
  const {config: initialOptions, configPath} = await readInitialOptions(

View on GitHub (pinned to 8e6d128e4a)