vitejs/vite · error · Error
Cannot deep clone non-plain object
Error message
Cannot deep clone non-plain object
What it means
Vite's deepClone utility recursively clones arrays, plain objects (via isObject), functions (passed by reference), and RegExps. Any other non-null object that is not a plain object or array — e.g. a Map, Set, Date, class instance, Error, URL, or Buffer — falls through to the final branch and throws, because Vite cannot safely deep-clone arbitrary structured objects without risking data loss or prototype issues.
Source
Thrown at packages/vite/src/node/utils.ts:1231
export function deepClone<T>(value: T): DeepWritable<T> {
if (Array.isArray(value)) {
return value.map((v) => deepClone(v)) as DeepWritable<T>
}
if (isObject(value)) {
const cloned: Record<string, any> = {}
for (const key in value) {
cloned[key] = deepClone(value[key])
}
return cloned as DeepWritable<T>
}
if (typeof value === 'function') {
return value as DeepWritable<T>
}
if (value instanceof RegExp) {
return new RegExp(value) as DeepWritable<T>
}
if (typeof value === 'object' && value != null) {
throw new Error('Cannot deep clone non-plain object')
}
return value as DeepWritable<T>
}
type MaybeFallback<D, V> = undefined extends V ? Exclude<V, undefined> | D : V
type MergeWithDefaultsResult<D, V> =
Equal<D, undefined> extends true
? V
: D extends Function | Array<any>
? MaybeFallback<D, V>
: V extends Function | Array<any>
? MaybeFallback<D, V>
: D extends Record<string, any>
? V extends Record<string, any>
? {
[K in keyof D | keyof V]: K extends keyof D
? K extends keyof VView on GitHub (pinned to 89620f09af)
Solutions
- Replace the non-plain object with a plain object literal (convert Map/Set to arrays/objects, Date to ISO string, class instance to a plain serializable shape).
- If the value is meant to be shared by reference, pass a function instead — deepClone passes functions through without cloning.
- Restructure so the non-plain object is constructed lazily inside a plugin hook rather than stored in cloned config.
Example fix
// before — Date instance fails deepClone
const config = {
myPlugin: { startedAt: new Date() },
}
// after — store as ISO string, reconstruct when needed
const config = {
myPlugin: { startedAt: new Date().toISOString() },
} Defensive patterns
Strategy: type-guard
Validate before calling
// Validate config values are plain-serializable before passing to Vite
function isPlainlyCloneable(v: unknown): boolean {
if (v === null || typeof v !== 'object') return true
if (v instanceof RegExp || typeof v === 'function') return true
if (Array.isArray(v)) return v.every(isPlainlyCloneable)
const proto = Object.getPrototypeOf(v)
if (proto !== Object.prototype && proto !== null) return false
return Object.values(v).every(isPlainlyCloneable)
}
if (!isPlainlyCloneable(myConfigValue)) {
throw new Error('Config contains a non-plain object that Vite cannot deep-clone')
} Type guard
function isPlainObject(v: unknown): v is Record<string, unknown> {
if (v === null || typeof v !== 'object') return false
const proto = Object.getPrototypeOf(v)
return proto === Object.prototype || proto === null
} Prevention
- Keep Vite config values to plain objects, arrays, primitives, functions, and RegExps only.
- Convert Date/Map/Set/class instances to plain serializable forms before placing in config.
- Store complex objects lazily inside plugin hooks rather than in cloned config.
When it happens
Trigger: Passing a config value (or any object processed by deepClone) that is a class instance, Map, Set, Date, Error, URL, or other built-in non-plain object. deepClone is used internally when normalizing/merging certain config structures, so a custom object placed where Vite expects a plain record triggers it.
Common situations: Putting a class instance, Date, Map, or Set into Vite config (e.g. a plugin option, define value, or resolved config field) that gets deep-cloned. Passing a third-party library object (like a compiled schema or AST node instance) where a plain config object is expected. Custom resolvers that attach non-plain metadata.
Related errors
- { runtime: "${result.runtime}" } is not supported for assets
- `renderLegacyChunks` and `renderModernChunks` cannot be both
- @vitejs/plugin-legacy does not support library mode.
- HMR is not supported by this runner transport, but `hmr` opt
- Invalid environment name "${name}". Environment names must o
AI-assisted analysis of vitejs/vite@89620f09af (2026-08-03).
Data as JSON: /data/errors/1473b1ba0c630b81.json.
Report an issue: GitHub.