vitejs/vite · error · Error
Cannot deep clone non-plain object
Error message
Cannot deep clone non-plain object
What it means
Thrown by deepClone() for a value that is an object (typeof 'object', non-null) but is not an array, not a plain {Object} (Object.prototype.toString !== '[object Object]'), not a function, and not a RegExp. deepClone only knows how to structurally copy plain data; instances of Date, Map, Set, Error, class instances, etc. are rejected rather than silently mishandled.
Solutions
- Convert non-plain values to plain equivalents before cloning (Date -> ISO string / number, Map -> plain object, Set -> array).
- Avoid putting class instances or built-in non-plain objects into config that Vite will deep-clone.
- If you call deepClone yourself, restrict inputs to JSON-shaped data (objects, arrays, primitives, RegExp).
Example fix
// before
const cfg = { lastBuilt: new Date() }
deepClone(cfg) // throws
// after
const cfg = { lastBuilt: Date.now() }
deepClone(cfg) // ok Defensive patterns
Strategy: type-guard
Validate before calling
function isDeepCloneable(v: unknown): boolean {
if (v == null || typeof v !== 'object') return true
if (Array.isArray(v)) return v.every(isDeepCloneable)
if (Object.prototype.toString.call(v) === '[object Object]')
return Object.values(v).every(isDeepCloneable)
return v instanceof RegExp || typeof v === 'function'
} Type guard
function isPlainCloneable(v: unknown): boolean {
if (v == null) return true
if (typeof v !== 'object') return true
if (Array.isArray(v)) return true
if (Object.prototype.toString.call(v) === '[object Object]') return true
if (v instanceof RegExp) return true
return false
} Prevention
- Keep config values JSON-shaped; convert Date/Map/Set to primitives before passing to Vite.
- Don't embed class instances in plugin options that Vite will clone.
When it happens
Trigger: Passing a config/value containing a Date, Map, Set, Error, Promise, ArrayBuffer, TypedArray, URL, or any class instance to deepClone(). The fallthrough branch at utils.ts:1230-1232 catches all other object types.
Common situations: deepClone is used internally when cloning resolved config / user options; passing a non-serializable value (e.g. a Date in a plugin option, a Map for aliases, a class instance) triggers it. Calling deepClone directly on plugin state that holds instances.
Related errors
- Cannot merge config in form of callback
- Config was resolved for a "build", expected a "serve"…
- FetchableDevEnvironment requires a `handleRequest` method…
- There is already a server associated with the config.
- transport must implement send and connect when invoke is…
AI-assisted analysis of vitejs/vite@b4d66fee14 (2026-08-11).
Data as JSON: /api/errors/1473b1ba0c630b81.
Report an issue: GitHub.
Appendix: 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 b4d66fee14)