Installation
To upgrade to Zod 4:Note — Zod 3 exported a number of undocumented quasi-internal utility types and functions that are not considered part of the public API. Changes to those are not documented here.
Unofficial codemod — A community-maintained codemod
zod-v3-to-v4 is available.Breaking Changes
Error Customization
Error Customization
Zod 4 standardizes the APIs for error customization under a single, unified Deprecates
Replaces Drops
The Drops
This is renamed to
error param. Previously Zod’s error customization APIs were fragmented and inconsistent.Deprecates message parameter
Replaces message param with error. The old message parameter is still supported but deprecated.- Zod 4
- Zod 3
Drops invalid_type_error and required_error
The invalid_type_error / required_error params have been dropped. These can now be cleanly represented with the new error parameter.- Zod 4
- Zod 3
Drops errorMap
This is renamed to error. Error maps can also now return a plain string (instead of {message: string}). They can also return undefined, which tells Zod to yield control to the next error map in the chain.- Zod 4
- Zod 3
ZodError Changes
ZodError Changes
Updates issue formats
The issue formats have been dramatically streamlined.Changes error map precedence
Deprecates .format() and .flatten()
The .format() and .flatten() methods on ZodError have been deprecated. Instead use the top-level z.treeifyError() function.Deprecates .addIssue() and .addIssues()
Directly push to err.issues array instead:Number Validations
Number Validations
No infinite values
.safe() no longer accepts floats
In Zod 4, z.number().safe() is deprecated. It now behaves identically to .int(), meaning it no longer accepts floats..int() accepts safe integers only
The z.number().int() API no longer accepts unsafe integers (outside the range of Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGER).String Validations
String Validations
Deprecates .email() etc
String formats are now represented as subclasses of ZodString. These APIs have been moved to the top-level z namespace.- Zod 4
- Zod 3
Stricter .uuid()
The z.uuid() now validates UUIDs more strictly against the RFC 9562/4122 specification. For a more permissive validator, use z.guid().Drops z.string().ip() and z.string().cidr()
- Zod 4
- Zod 3
Coercion Changes
Coercion Changes
The input type of all
z.coerce schemas is now unknown.Default Values
Default Values
Object Schema Changes
Object Schema Changes
Defaults applied within optional fields
Deprecates .strict() and .passthrough()
Use the top-level z.strictObject() and z.looseObject() functions instead:- Zod 4
- Zod 3
Deprecates .merge()
The .merge() method has been deprecated in favor of .extend():Drops .deepPartial()
This long-deprecated method has been removed with no direct replacement.Changes z.unknown() optionality
Enum Changes
Enum Changes
Array Changes
Array Changes
Function Schema Changes
Function Schema Changes
Refinement Changes
Refinement Changes
Record Changes
Record Changes
Other Breaking Changes
Other Breaking Changes
z.promise() deprecated
If you have an input that may be a Promise, just await it before parsing with Zod.z.literal() drops symbol support
Symbols are no longer considered literal values.Static .create() factories dropped
Previously all Zod classes defined a static .create() method. These are now implemented as standalone factory functions.z.intersection() throws Error on merge conflict
Drops convenience methods
The undocumented convenience methodsz.ostring(), z.onumber(), etc. have been removed.Internal Changes
Updates generics
The generic structure ofZodType has changed:
Adds z.core
Many utility functions and types have been moved to the new zod/v4/core sub-package:
Moves ._def
The ._def property is now moved to ._zod.def.
Drops ZodEffects
Refinements now live inside schemas themselves as “checks”. Transforms have been moved to a dedicated ZodTransform class.
Drops ZodPreprocess
The z.preprocess() function now returns a ZodPipe instance:
Drops ZodBranded
Branding is now handled with a direct modification to the inferred type, instead of a dedicated class.
Migration Steps
- Update dependencies: Install
zod@^4.0.0 - Run the codemod: Consider using
zod-v3-to-v4for automated migration - Update error handling: Replace
message,invalid_type_error,required_error, anderrorMapwith the unifiederrorparameter - Update string validations: Replace method calls like
.email()with top-level functions likez.email() - Update object schemas: Replace
.merge()with.extend(), and.strict()/.passthrough()withz.strictObject()/z.looseObject() - Update function schemas: Restructure to use the new
input/outputAPI - Test thoroughly: Run your test suite to catch any edge cases