colinhacks/zod · error · Error
.partial() cannot be used on object schemas containing…
Error message
.partial() cannot be used on object schemas containing refinements
What it means
Thrown by partial() when the source object has refinements. `.partial()` wraps selected (or all) fields in an optional wrapper and rebuilds the schema with an empty checks array, which would silently discard object-level refinements. Like `.pick()`/`.omit()`, the operation is blocked rather than losing the invariants.
Solutions
- Apply `.partial()` to a refinement-free base, then re-apply the refinement to the result.
- Build the partial schema from scratch with `z.object({ a: z.string().optional() })` and add only the refinements that still make sense.
- Move the refinement to leaf fields so it survives the partial() rebuild.
Example fix
// before
const S = z.object({ a: z.string(), b: z.string() }).refine(v => v.a !== v.b);
const P = S.partial(); // throws
// after
const P = z.object({ a: z.string().optional(), b: z.string().optional() })
.refine(v => (v.a ?? '') !== (v.b ?? '')); Defensive patterns
Strategy: validation
Validate before calling
function canPartial(schema: z.ZodObject): boolean {
const checks = (schema as any)?._zod?.def?.checks;
return !checks || checks.length === 0;
}
// if (!canPartial(S)) { /* rebuild from a refinement-free base */ } Type guard
function hasObjectRefinements(schema: z.ZodObject): boolean {
const checks = (schema as any)?._zod?.def?.checks;
return Array.isArray(checks) && checks.length > 0;
} Prevention
- Build PATCH/update schemas from a refinement-free base, then re-apply invariants.
- Move cross-field invariants to leaf-level `.refine()` when you need both partial() and validation.
- Centralize 'input vs entity' schema variants so refinements are not lost during transforms.
When it happens
Trigger: Calling `myObject.partial()` or `myObject.partial({ a: true })` on an object schema that has `.refine()`/`.check()` attached at the object node. Eager throw at `.partial()` call time.
Common situations: Building a PATCH/update schema from a full entity that carries cross-field invariants; making a form-input schema optional where the source has validation rules; layering `.partial()` over a schema that gained a refinement after a refactor.
Related errors
- Cannot overwrite keys on object schemas containing…
- .merge() cannot be used on object schemas containing…
- .omit() cannot be used on object schemas containing…
- .pick() cannot be used on object schemas containing…
- Invalid input to extend: expected a plain object
AI-assisted analysis of colinhacks/zod@2d90846af9 (2026-08-11).
Data as JSON: /api/errors/f7b87486aa94bc94.
Report an issue: GitHub.
Appendix: source
Thrown at packages/zod/src/v4/core/util.ts:729
get catchall() {
return b._zod.def.catchall;
},
checks: b._zod.def.checks ?? [],
});
return clone(a, def) as any;
}
export function partial(
Class: SchemaClass<schemas.$ZodOptional> | null,
schema: schemas.$ZodObject,
mask: object | undefined
): any {
const currDef = schema._zod.def;
const checks = currDef.checks;
const hasChecks = checks && checks.length > 0;
if (hasChecks) {
throw new Error(".partial() cannot be used on object schemas containing refinements");
}
const def = mergeDefs(schema._zod.def, {
get shape() {
const oldShape = schema._zod.def.shape;
const shape: Writeable<schemas.$ZodShape> = { ...oldShape };
if (mask) {
for (const key in mask) {
if (!(key in oldShape)) {
throw new Error(`Unrecognized key: "${key}"`);
}
if (!(mask as any)[key]) continue;
// if (oldShape[key]!._zod.optin === "optional") continue;
shape[key] = Class
? new Class({
type: "optional",
innerType: oldShape[key]!,View on GitHub (pinned to 2d90846af9)