vitejs/vite · error · Error

Cannot merge config in form of callback

Error message

Cannot merge config in form of callback

What it means

Vite's mergeConfig merges two already-resolved plain config objects recursively. It explicitly rejects either argument being a function because config 'callbacks' (the defineConfig(() => {}) form) are meant to be resolved/awaited before merging — merging unresolved callback forms is undefined. Passing a function as either defaults or overrides indicates the caller bypassed the env/mode resolution step.

Source

Thrown at packages/vite/src/node/utils.ts:1575

      )
      continue
    }

    merged[key] = value
  }
  return merged
}

export function mergeConfig<
  D extends Record<string, any>,
  O extends Record<string, any>,
>(
  defaults: D extends Function ? never : D,
  overrides: O extends Function ? never : O,
  isRoot = true,
): Record<string, any> {
  if (typeof defaults === 'function' || typeof overrides === 'function') {
    throw new Error(`Cannot merge config in form of callback`)
  }

  return mergeConfigRecursively(defaults, overrides, isRoot ? '' : '.')
}

function mergeInput(a?: InputOption, b?: InputOption): InputOption | undefined {
  if (!a) return b
  if (!b) return a

  if (typeof a === 'string' && typeof b === 'string') {
    return [a, b]
  }
  if (Array.isArray(a) && (typeof b === 'string' || Array.isArray(b))) {
    return [...a, ...(Array.isArray(b) ? b : [b])]
  }
  if (Array.isArray(b) && (typeof a === 'string' || Array.isArray(a))) {
    return [...(Array.isArray(a) ? a : [a]), ...b]
  }

View on GitHub (pinned to 89620f09af)

Solutions

  1. Resolve the callback first: call the function with ({ mode, command }) to get a plain object, then pass that to mergeConfig.
  2. Export the base config as a plain object (not the callback form of defineConfig) if it will be merged programmatically.
  3. Use defineConfig's built-in merging or the loadConfig/resolveConfig pipeline so callbacks are resolved before mergeConfig runs.

Example fix

// before — passing the callback form to mergeConfig
const base = defineConfig(() => ({ plugins: [a] }))
const merged = mergeConfig(base, { plugins: [b] })

// after — resolve the callback, then merge plain objects
const baseResolved = await (base as any)({ mode: 'development', command: 'serve' })
const merged = mergeConfig(baseResolved, { plugins: [b] })
Defensive patterns

Strategy: validation

Validate before calling

// Ensure neither argument is a function before merging
if (typeof defaults === 'function' || typeof overrides === 'function') {
  throw new Error('Resolve defineConfig callbacks to plain objects before mergeConfig')
}
const merged = mergeConfig(defaults, overrides)

Type guard

function isPlainConfig(v: unknown): v is Record<string, any> {
  return v != null && typeof v === 'object' && typeof v !== 'function'
}

Prevention

When it happens

Trigger: Calling mergeConfig(defineConfig(() => ({...})), {...}) or mergeConfig({...}, defineConfig(() => ({...}))) directly — i.e. passing the function form of defineConfig (or any function) as an argument. This happens when merging configs programmatically without first resolving the callback.

Common situations: Writing a plugin or framework layer that merges Vite configs and accidentally passes the defineConfig callback form instead of its resolved output. Combining a shared base config (exported as a callback) with an override config via mergeConfig. Copy-pasting from a config file that uses defineConfig(() => ...) into a mergeConfig call site.

Related errors


AI-assisted analysis of vitejs/vite@89620f09af (2026-08-03). Data as JSON: /data/errors/ccfbbd300b9a0876.json. Report an issue: GitHub.