Zod v4 Migration Checklist: API Changes and Dependabot Gotchas
Published: 2026-06-28
Dependabot opened a dependency upgrade PR β Zod 3.25.76 β 4.4.3 β in the same batch as a TypeScript 6.0 upgrade in the dev-tooling group.
Most of the breaking changes were straightforward. The following ones werenβt.
This is a demo project β I merged and fixed what broke, which is how most of this ended up documented.
ZodError.errors Alias Removed β Use .issues
The most visible API change in Zod v4 is the removal of the .errors alias on ZodError. This v3 pattern breaks on upgrade:
// v3
catch (e) {
if (e instanceof ZodError) {
console.log(e.errors);
}
}
In v4, that line produces: Property 'errors' does not exist on type 'ZodError'.
The .errors alias has been removed. .issues was the primary property in v3 and remains so in v4:1
// v4
catch (e) {
if (e instanceof ZodError) {
console.log(e.issues);
}
}
No behavior change β the data is the same array. Before upgrading, search the codebase for every .errors access on ZodError instances and update them to .issues.
parse() Return Type Changed β and It Compounds with TypeScript 6
In Zod v3, schema.parse() returned Output directly. In Zod v4, it returns core.output<this> β a this-polymorphic conditional type.2
For most call sites this is invisible. The failure mode is specific: ReturnType<typeof Schema.parse> collapses to unknown when combined with TypeScript 6.0βs inference changes. This is covered in detail in TypeScript 6 + Zod v4: ReturnType Collapses to unknown.
The migration fix applies regardless of the TypeScript version: stop using ReturnType<typeof Schema.parse> to derive the output type. Use z.infer<> instead:
// Before β fragile with Zod v4
type AlertDto = ReturnType<typeof AlertSchema.parse>;
// After β stable
type AlertDto = z.infer<typeof AlertSchema>;
Grep before merging:
grep -r "ReturnType<typeof" src/ --include="*.ts"
Any match involving a Zod schema method (parse, safeParse) needs to be replaced.
Dependabot Group Splits Hide Compound Failures
Dependabot split the TypeScript and Zod upgrades into separate PRs. Each PRβs CI run passed independently β type inference failures arenβt compile errors until they cause a downstream type mismatch.
The combination of TypeScript 6.0 and Zod v4 only became visible during the production build, after both had merged. By then, isolating which PR introduced each error was manual work.
Two practices that would have caught this earlier:
Read the CHANGELOG for every version bump, not just major ones. Dependabot marks major upgrades clearly, but minor bumps can contain runtime-breaking changes that produce no TypeScript error and pass CI. When zod goes from 3.x to 4.x, treat it as a mandatory review before merging.
Run tsc --noEmit after each group merges, before merging the next. Type-level regressions are invisible to tests. A type-check pass between PRs narrows the search space before failures compound.
Migration Checklist
Before merging a Zod v3 β v4 upgrade:
- Search for
.errorsonZodErrorinstances β update to.issues - Search for
ReturnType<typeof schema.parse>orReturnType<typeof schema.safeParse>β replace withz.infer<typeof schema>or a named exported type - If TypeScript 6.0 is also in scope: read the ReturnType interaction post before merging
- For grouped Dependabot PRs: read the CHANGELOG for each package individually
- Run
tsc --noEmitafter each major bump group merges
Further Reading
- Zod v4 migration guide β the authoritative list of breaking changes, including the removal of
.errors,ZodTypegeneric restructure, and the fullparse()signature change. - TypeScript 6 + Zod v4: ReturnType collapses to unknown β detailed walkthrough of why
ReturnType<typeof Schema.parse>becomesunknownwhen both upgrades land together. - TypeScript 6.0: 3 Breaking Changes That Hit My NestJS Project β the TS6 changes that came with the same upgrade batch:
rootDirenforcement,baseUrldeprecation, andstrictPropertyInitialization.
Footnotes
-
Zod v4 migration guide β
ZodError.errorsdropped..errorswas an alias for.issuesin Zod v3 that was removed in v4..issueswas the primary property all along; use it directly. β© -
parse()method signature in Zod v4 βpackages/zod/src/v4/classic/schemas.ts#L116. The return type changed fromOutputtocore.output<this>(this-polymorphic). β©