vitejs/vite · error · Error
Cannot merge config in form of callback
Error message
Cannot merge config in form of callback
What it means
Thrown by mergeConfig() when either the defaults or the overrides argument is a function. mergeConfig merges two plain config objects; the functional form of defineConfig/mergeConfig (which returns a function) is not accepted here and must be resolved to a plain object first.
Solutions
- Resolve the function to a plain object before merging: invoke the defineConfig callback (await if async) to get the config object.
- Use mergeConfig with plain objects only; do not pass the callback form as an argument.
- If you need the functional form, call mergeConfig(callback) with a single function argument at the top level instead of merging two values.
Example fix
// before
const base = defineConfig(async () => ({ plugins: [a] }))
mergeConfig(base, { plugins: [b] }) // throws (base is a function)
// after
const baseObj = await base()
mergeConfig(baseObj, { plugins: [b] }) Defensive patterns
Strategy: type-guard
Validate before calling
function isPlainConfig(v: unknown): v is Record<string, any> {
return v != null && typeof v !== 'function'
}
if (!isPlainConfig(defaults) || !isPlainConfig(overrides)) {
throw new TypeError('mergeConfig needs plain objects, not functions')
} Type guard
function isPlainConfigObject(v: unknown): v is Record<string, any> {
return v != null && typeof v === 'object' && typeof v !== 'function'
} Prevention
- Resolve defineConfig/mergeConfig callbacks to plain objects before calling two-arg mergeConfig.
- Reserve the functional form for the single-argument mergeConfig(callback) entry point.
When it happens
Trigger: Calling mergeConfig(fn, overrides) or mergeConfig(defaults, fn) where fn is a function — e.g. the result of defineConfig(async () => ({...})) or a mergeConfig callback. The check `typeof ... === 'function'` is at utils.ts:1574.
Common situations: Programmatically merging a base config that was authored with the callback form of defineConfig. Passing an un-invoked mergeConfig callback (mergeConfig also supports mergeConfig(callback) at the top level, but two-argument mergeConfig does not).
Related errors
- Cannot deep clone non-plain object
- Config was resolved for a "build", expected a "serve"…
- FetchableDevEnvironment `context.handleRequest` must return…
- FetchableDevEnvironment `dispatchFetch` must receive a…
- FetchableDevEnvironment requires a `handleRequest` method…
AI-assisted analysis of vitejs/vite@b4d66fee14 (2026-08-11).
Data as JSON: /api/errors/ccfbbd300b9a0876.
Report an issue: GitHub.
Appendix: 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 b4d66fee14)