Zod v4 introduces 20+ breaking changes across its API surface — from renamed methods and restructured error handling to silent behavioral shifts that can break your application without compiler warnings. This guide covers every change and shows how SchemaShift automates the upgrade with AST-based transforms.
Migrate your entire src directory from Zod v3 to v4 with a single command:
schemashift migrate ./src -f zod-v3 -t v4
Preview changes first without modifying any files:
schemashift migrate ./src -f zod-v3 -t v4 --dry-run
Add --verbose to see every transformation applied to each file,
or --output-diff - to pipe a unified diff to stdout.
Side-by-side comparison of the most common Zod v3 patterns and their v4 equivalents. SchemaShift handles all of these automatically.
// Implicit string key
const Config = z.record(z.number());
// Explicit key type required
const Config = z.record(z.string(), z.number());
const Full = Base.merge(Extra);
const Full = Base.extend(Extra.shape);
const Status = z.nativeEnum(MyEnum);
const Status = z.enum(MyEnum);
schema.superRefine((val, ctx) => {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Invalid',
});
});
schema.check((val, ctx) => {
ctx.issues.push({
code: z.ZodIssueCode.custom,
message: 'Invalid',
});
});
catch (err) {
console.log(err.errors);
}
catch (err) {
console.log(err.issues);
}
z.string({
invalid_type_error: 'Must be a string',
required_error: 'Field is required',
});
z.string({
error: 'Must be a string',
});
Every automatic transform that SchemaShift applies during the Zod v3 to v4 migration:
| Zod v3 Pattern | Zod v4 Equivalent | Type |
|---|---|---|
z.record(valueSchema) |
z.record(z.string(), valueSchema) |
Auto-transform |
.merge(other) |
.extend(other.shape) |
Auto-transform |
z.nativeEnum(MyEnum) |
z.enum(MyEnum) |
Auto-transform |
.superRefine(fn) |
.check(fn) with ctx.issues.push() |
Auto-transform |
error.errors |
error.issues |
Auto-transform |
invalid_type_error |
error (unified param) |
Auto-transform |
required_error |
error (unified param) |
Auto-transform |
.strip() |
Removed (default behavior in v4) | Auto-removed |
z.preprocess(fn, schema) |
z.pipe(z.unknown(), z.transform(fn), schema) |
Auto-transform |
error.flatten() |
z.flattenError(error) |
Auto-transform |
error.format() |
z.treeifyError(error) |
Auto-transform |
._def |
._zod.def |
Auto-transform |
.strict() |
z.strictObject() |
Deprecation warning |
.passthrough() |
z.looseObject() |
Deprecation warning |
ZodType, ZodSchema, etc. |
Import from zod/v4/core |
Warning |
These changes are not simple find-and-replace. They involve subtle behavioral shifts that can cause runtime bugs without any TypeScript errors:
instanceof Error to catch Zod validation errors will silently
stop matching. Switch to instanceof ZodError or check for error.issues.
.refine() before .transform() short-circuited on failure.
In v4, the transform executes regardless, which may cause unexpected results or runtime errors
if the transform assumes validated data.
z.string().default('hello').optional() returned undefined when
no value was provided. In v4, the default value is applied even for optional fields. Review all
combinations where .default() is chained with .optional().
.default(), the interaction between .catch() and
.optional() has changed in v4. Test all schemas using this combination.
.pipe() chains. Workaround
guidance is provided in TODO comments for schemas that need adjustment.
z.strictObject() and z.looseObject() instead. The old methods
still work but will be removed in a future release.
ZodType, ZodSchema, ZodRawShape, or
similar utility types, they now live in zod/v4/core instead of the main
zod entrypoint.
Run a dry-run first to preview every change without modifying your codebase:
schemashift migrate ./src -f zod-v3 -t v4 --dry-run --verbose
Export a unified diff for code review before applying:
schemashift migrate ./src -f zod-v3 -t v4 --dry-run --output-diff v4-migration.patch
When you are ready, run the full migration with a backup:
schemashift migrate ./src -f zod-v3 -t v4
SchemaShift creates a timestamped backup before every migration. Use
schemashift rollback to restore originals if anything goes wrong.
For large codebases, use canary mode to migrate a small percentage first:
schemashift migrate ./src -f zod-v3 -t v4 --canary 20
.strict(), .passthrough())instanceof Error checks against ZodError.default().optional() combinations behave as expected.transform() after .refine() chains for safety.catch().optional() combinationszod-validation-error to v5.0.0+drizzle-zod to a v4-compatible version@trpc/server compatibility (v11 required for Zod v4)@hookform/resolvers if using react-hook-formzod-openapi / @asteasolutions/zod-to-openapi compatibilityzod-prisma / zod-prisma-types schemas if applicableschemashift doctor to verify project health post-migration
Zod v4 introduces 20+ breaking changes including renamed methods (.superRefine()
→ .check()), restructured error handling (error.errors →
error.issues), removed methods (.strip()), unified error params,
and several silent behavioral changes around .default(), .catch(),
and .transform() ordering.
Yes. SchemaShift provides an AST-based migration tool that automatically transforms
12+ patterns. Run schemashift migrate ./src -f zod-v3 -t v4 to convert your
codebase. Use --dry-run first to preview all changes without modifying files.
No. In Zod v4, ZodError no longer extends the built-in Error class.
Any code that uses instanceof Error to catch ZodError will stop
matching. Use instanceof ZodError or check for error.issues instead.
In v3, z.string().default('hello').optional() returned undefined
when no value was provided. In v4, the default value is applied even when the field is optional.
This is a silent change with no compiler warning — review all such combinations carefully.
Key packages: zod-validation-error (upgrade to v5.0.0+),
drizzle-zod (needs v4-compatible version),
@trpc/server (v11 required),
@hookform/resolvers (matching version needed),
and zod-openapi, zod-prisma, zodios,
zod-form-data may need updates. Run schemashift doctor to detect these automatically.
Zod v3 to v4 migration requires the Individual tier ($49 one-time) or higher. The free tier
supports Yup → Zod and Joi → Zod migrations. Run
schemashift pricing for full details.
Stop spending hours on manual find-and-replace. SchemaShift handles 20+ Zod v4 breaking changes automatically with full backup and rollback support.
npm install -g schemashift-cli