colinhacks/zod · error · Error
Invalid input to extend: expected a plain object
Error message
Invalid input to extend: expected a plain object
What it means
Thrown by `.extend()` when the argument is not a plain object — checked via `isPlainObject(shape)` at util.ts:660. `.extend()` needs a literal shape map of `ZodType` values; passing arrays, class instances, `null`, `undefined`, or anything with a non-standard prototype is rejected upfront. This prevents silent spread-merge of unexpected inputs into the schema's shape.
Source
Thrown at packages/zod/src/v4/core/util.ts:661
if (!(key in currDef.shape)) {
throw new Error(`Unrecognized key: "${key}"`);
}
if (!(mask as any)[key]) continue;
delete newShape[key];
}
assignProp(this, "shape", newShape); // self-caching
return newShape;
},
checks: [],
});
return clone(schema, def);
}
export function extend(schema: schemas.$ZodObject, shape: schemas.$ZodShape): any {
if (!isPlainObject(shape)) {
throw new Error("Invalid input to extend: expected a plain object");
}
const checks = schema._zod.def.checks;
const hasChecks = checks && checks.length > 0;
if (hasChecks) {
// Only throw if new shape overlaps with existing shape
// Use getOwnPropertyDescriptor to check key existence without accessing values
const existingShape = schema._zod.def.shape;
for (const key in shape) {
if (Object.getOwnPropertyDescriptor(existingShape, key) !== undefined) {
throw new Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.");
}
}
}
const def = mergeDefs(schema._zod.def, {
get shape() {
const _shape = { ...schema._zod.def.shape, ...shape };View on GitHub (pinned to 912f0f51b0)
Solutions
- Wrap the input as an object literal: `schema.extend({ field: z.string() })` rather than `schema.extend(z.string())`.
- If building dynamically from entries, convert with `Object.fromEntries(entries)` before passing.
- If you intended to merge two object schemas, use `.merge(other)` instead of `.extend(other)`.
Example fix
// before
const Extended = User.extend(z.string());
// after
const Extended = User.extend({ nickname: z.string() }); Defensive patterns
Strategy: type-guard
Validate before calling
function isPlainShape(v: unknown): v is Record<string, z.core.$ZodType> {
return Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null;
}
if (!isPlainShape(arg)) throw new Error('shape must be a plain object');
const Out = schema.extend(arg); Type guard
function isZodShape(v: unknown): v is z.core.$ZodShape {
return typeof v === 'object' && v !== null &&
(Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null) &&
Object.values(v).every(x => x && typeof x === 'object' && '_zod' in x);
} Prevention
- Always pass object literals `{ field: z.X() }`, not single schemas or arrays.
- Build dynamic shapes via `Object.fromEntries(entries)`.
When it happens
Trigger: Calling `schema.extend(someArray)` or `schema.extend(someZodType)` or `schema.extend(JSON.parse(json))` where the result is not a plain object literal of field schemas.
Common situations: Dynamically building a shape from `Object.values()` (array) and passing it directly; passing a single `z.string()` instead of `{ field: z.string() }`; passing a `Map` or other iterable; deserializing shapes from JSON without reconstructing Zod types.
Related errors
- Cannot overwrite keys on object schemas containing refinemen
- Invalid input to safeExtend: expected a plain object
- Unrecognized key: "${key}"
- .omit() cannot be used on object schemas containing refineme
- .merge() cannot be used on object schemas containing refinem
AI-assisted analysis of colinhacks/zod@912f0f51b0 (2026-08-03).
Data as JSON: /data/errors/5c01fb2600917b9b.json.
Report an issue: GitHub.