jestjs/jest · error · TypeError

Cannot merge config in form of callback

Error message

Cannot merge config in form of callback

What it means

jest-config's `mergeConfig` (packages/jest-config/src/index.ts:66) does a deepmerge of two config objects, so it must inspect their properties; it throws a TypeError if either argument is a function (a 'callback-style' config), because a function is opaque until called and merging it is undefined behavior. The runtime guard is at index.ts:73.

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 f49721c78e)

Solutions

  1. Resolve the callback yourself first: `const base = typeof baseOrFn === 'function' ? await baseOrFn() : baseOrFn;` then call `mergeConfig(base, overrides)`.
  2. Author the base config as a plain object (not a function export) when you intend to merge it.
  3. Use `defineConfig` only as a type helper; it does not unwrap callbacks for merging.

Example fix

// before
const base = () => ({ testEnvironment: 'node' });
module.exports = mergeConfig(base, { verbose: true }); // throws

// after
const base = { testEnvironment: 'node' };
module.exports = mergeConfig(base, { verbose: true });
Defensive patterns

Strategy: validation

Validate before calling

import {mergeConfig} from 'jest-config';
function safeMerge(d: any, o: any) {
  if (typeof d === 'function' || typeof o === 'function') {
    throw new TypeError('Cannot merge a callback config; resolve it to an object first');
  }
  return mergeConfig(d, o);
}

Type guard

const isPlainConfigObject = (c: unknown): c is Record<string, unknown> =>
  c !== null && typeof c === 'object' && typeof (c as any) !== 'function';

Try / catch

try {
  return mergeConfig(defaults, overrides);
} catch (e) {
  if (e instanceof TypeError && /callback/.test(e.message)) {
    // resolve callbacks to objects, then retry with plain objects
    const d = typeof defaults === 'function' ? await defaults() : defaults;
    const o = typeof overrides === 'function' ? await overrides() : overrides;
    return mergeConfig(d, o);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `mergeConfig(() => baseConfig, overrides)` or `mergeConfig(base, async () => overrides)` — passing a function config (the form supported by jest.config.js exports) into mergeConfig.

Common situations: Building a base config programmatically and forwarding the callback export directly to mergeConfig; wrapping a vendor config that exports a function.

Related errors


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