Upgrade Zod v3 to v4

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.

Quick Start

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
Tip

Add --verbose to see every transformation applied to each file, or --output-diff - to pipe a unified diff to stdout.

Before & After

Side-by-side comparison of the most common Zod v3 patterns and their v4 equivalents. SchemaShift handles all of these automatically.

z.record() — explicit key type

Zod v3
// Implicit string key
const Config = z.record(z.number());
Zod v4
// Explicit key type required
const Config = z.record(z.string(), z.number());

.merge() → .extend()

Zod v3
const Full = Base.merge(Extra);
Zod v4
const Full = Base.extend(Extra.shape);

z.nativeEnum() → z.enum()

Zod v3
const Status = z.nativeEnum(MyEnum);
Zod v4
const Status = z.enum(MyEnum);

.superRefine() → .check()

Zod v3
schema.superRefine((val, ctx) => {
  ctx.addIssue({
    code: z.ZodIssueCode.custom,
    message: 'Invalid',
  });
});
Zod v4
schema.check((val, ctx) => {
  ctx.issues.push({
    code: z.ZodIssueCode.custom,
    message: 'Invalid',
  });
});

error.errors → error.issues

Zod v3
catch (err) {
  console.log(err.errors);
}
Zod v4
catch (err) {
  console.log(err.issues);
}

Error message params

Zod v3
z.string({
  invalid_type_error: 'Must be a string',
  required_error: 'Field is required',
});
Zod v4
z.string({
  error: 'Must be a string',
});

Full Conversion Reference

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

Edge Cases and Gotchas

These changes are not simple find-and-replace. They involve subtle behavioral shifts that can cause runtime bugs without any TypeScript errors:

Automated Migration with SchemaShift

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
Automatic Backup

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

Migration Checklist

Frequently Asked Questions

What breaking changes does Zod v4 introduce?

Zod v4 introduces 20+ breaking changes including renamed methods (.superRefine().check()), restructured error handling (error.errorserror.issues), removed methods (.strip()), unified error params, and several silent behavioral changes around .default(), .catch(), and .transform() ordering.

Can I automate the Zod v3 to v4 migration?

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.

Does ZodError still extend Error in Zod v4?

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.

How do .default() and .optional() behave differently in v4?

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.

What ecosystem packages need updating?

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.

Is the Zod v3 to v4 migration available in the free tier?

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.

Related Guides

Ready to Upgrade?

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

Get Individual License — $49 View All Plans