{"id":"ccfbbd300b9a0876","repo":"vitejs/vite","slug":"cannot-merge-config-in-form-of-callback","errorCode":null,"errorMessage":"Cannot merge config in form of callback","messagePattern":"Cannot merge config in form of callback","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vite/src/node/utils.ts","lineNumber":1575,"sourceCode":"      )\n      continue\n    }\n\n    merged[key] = value\n  }\n  return merged\n}\n\nexport function mergeConfig<\n  D extends Record<string, any>,\n  O extends Record<string, any>,\n>(\n  defaults: D extends Function ? never : D,\n  overrides: O extends Function ? never : O,\n  isRoot = true,\n): Record<string, any> {\n  if (typeof defaults === 'function' || typeof overrides === 'function') {\n    throw new Error(`Cannot merge config in form of callback`)\n  }\n\n  return mergeConfigRecursively(defaults, overrides, isRoot ? '' : '.')\n}\n\nfunction mergeInput(a?: InputOption, b?: InputOption): InputOption | undefined {\n  if (!a) return b\n  if (!b) return a\n\n  if (typeof a === 'string' && typeof b === 'string') {\n    return [a, b]\n  }\n  if (Array.isArray(a) && (typeof b === 'string' || Array.isArray(b))) {\n    return [...a, ...(Array.isArray(b) ? b : [b])]\n  }\n  if (Array.isArray(b) && (typeof a === 'string' || Array.isArray(a))) {\n    return [...(Array.isArray(a) ? a : [a]), ...b]\n  }","sourceCodeStart":1557,"sourceCodeEnd":1593,"githubUrl":"https://github.com/vitejs/vite/blob/89620f09afcfef6b35e7bb8660132ab5b4d0cd3b/packages/vite/src/node/utils.ts#L1557-L1593","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Resolve the callback first: call the function with ({ mode, command }) to get a plain object, then pass that to mergeConfig.","Export the base config as a plain object (not the callback form of defineConfig) if it will be merged programmatically.","Use defineConfig's built-in merging or the loadConfig/resolveConfig pipeline so callbacks are resolved before mergeConfig runs."],"exampleFix":"// before — passing the callback form to mergeConfig\nconst base = defineConfig(() => ({ plugins: [a] }))\nconst merged = mergeConfig(base, { plugins: [b] })\n\n// after — resolve the callback, then merge plain objects\nconst baseResolved = await (base as any)({ mode: 'development', command: 'serve' })\nconst merged = mergeConfig(baseResolved, { plugins: [b] })","handlingStrategy":"validation","validationCode":"// Ensure neither argument is a function before merging\nif (typeof defaults === 'function' || typeof overrides === 'function') {\n  throw new Error('Resolve defineConfig callbacks to plain objects before mergeConfig')\n}\nconst merged = mergeConfig(defaults, overrides)","typeGuard":"function isPlainConfig(v: unknown): v is Record<string, any> {\n  return v != null && typeof v === 'object' && typeof v !== 'function'\n}","tryCatchPattern":null,"preventionTips":["Always resolve defineConfig(() => ...) callbacks to plain objects before mergeConfig.","Export shared base configs as plain objects, not callback forms, when they will be merged.","Run mergeConfig only on already-resolved config objects from resolveConfig/loadConfig."],"tags":["config","merge","defineconfig"],"analyzedSha":"89620f09afcfef6b35e7bb8660132ab5b4d0cd3b","analyzedAt":"2026-08-03T19:28:02.920Z","schemaVersion":2}