Zod v3 to v4 Codemod

This codemod automatically migrates your Zod schemas and validation logic from Zod v3 to Zod v4, based on the official Zod v4 Changelog and API Reference.

✅ What it does

This codemod transforms deprecated or changed APIs to their v4 equivalents. It focuses on making your codebase compliant with Zod v4's improved and unified error handling, updated object and string APIs, and other key breaking changes.

🔧 Transforms Included

1. messageerror

Before:

z.string().min(5, { message: 'Too short.' });

After:

z.string().min(5, { error: 'Too short.' });

2. required_error / invalid_type_errorerror function

Before:

z.string({
  required_error: 'This field is required',
  invalid_type_error: 'Not a string',
});

After:

z.string({
  error: (issue) => issue.input === undefined 
    ? 'This field is required' 
    : 'Not a string'
});

3. errorMaperror

Before:

z.string({
  errorMap: (issue, ctx) => {
    if (issue.code === 'too_small') {
      return { message: `Value must be >${issue.minimum}` };
    }
    return { message: ctx.defaultError };
  },
});

After:

z.string({
  error: (issue) => {
    if (issue.code === 'too_small') {
      return `Value must be >${issue.minimum}`;
    }
  }
});

4. .strict(), .passthrough(), .strip()

Before:

z.object({ name: z.string() }).strict();
z.object({ name: z.string() }).passthrough();
z.object({ name: z.string() }).strip();

After:

z.strictObject({ name: z.string() });
z.looseObject({ name: z.string() });
z.object({ name: z.string() }); // strip is now default

5. Deprecated z.string().email() and similar → Top-level APIs

Before:

z.string().email();
z.string().uuid();
z.string().url();

After:

z.email();
z.uuid();
z.url();

6. z.record(schema)z.record(keySchema, valueSchema)

Before:

z.record(z.string());

After:

z.record(z.string(), z.string());

⚠️ This transformation assumes both key and value are strings. Customize as needed.

7. ZodError instanceof Error

ZodError no longer extends Error, so instanceof checks against Error may no longer work.

Before:

if (err instanceof z.ZodError || err instanceof Error) {}

After:

if (err instanceof z.ZodError) {}

💡 Other Considerations

While this codemod automates a large portion of the migration, you may still need to:

  • Manually inspect rare Zod APIs like .function(), .promise(), or .literal(Symbol)
  • Adjust error-handling logic or custom utilities that rely on ZodError internals

Refer to the full official changelog for advanced topics: