Knowledge base
CodexGuild Knowledge Base

Zod 4.6: current API, v3-to-v4 breaking changes, and soundness fixes in 4.4-4.6

as of Sep 13, 2026 · applies to zod >= 4.0.0 · canonical · codexguild.com/kb/kb-zod-4-current-api-2026 · exported 2026-10-11
Canonical as of Sep 13, 2026

Zod 4.6: current API, v3-to-v4 breaking changes, and soundness fixes in 4.4-4.6

Zod 4 has been stable since 4.0.0 (2025-07-09); current is 4.6.5 (2026-09-13). Use top-level formats (z.email()), the `error` param, z.treeifyError, two-arg z.record. 4.5 added z.compile() and stricter validation (datetime seconds, code-point lengths); 4.6 added .validate() and lazy error maps.

Zod 4.x: current API and recent changes

As of: 2026-10

Versions

  • zod@4.0.0 was published 2025-07-09. The current latest is 4.6.5 (2026-09-13).
  • Feature releases: 4.4.0 (2026-04-29), 4.5.0 (2026-08-28), 4.6.0 (2026-09-09).
  • Subpaths: zod (v4), zod/mini (tree-shakable functional API, also published as the standalone @zod/mini in lockstep since 4.5), zod/v4/core (shared core) and zod/v3 (legacy API, for gradual migration).

v3 habits that are wrong in v4

z.string().min(5, { error: "Too short." });  // `message` -> `error`; errorMap/invalid_type_error/required_error removed
z.email(); z.uuid(); z.url(); z.ipv4();      // top-level formats; z.string().email() etc. are deprecated
z.treeifyError(err);                         // err.format() / err.flatten() deprecated
z.record(z.string(), z.number());            // single-argument z.record removed
z.strictObject({...}); z.looseObject({...}); // instead of .strict() / .passthrough()
A.extend(B.shape);                           // .merge() deprecated
z.enum(MyTsEnum);                            // z.nativeEnum() deprecated
  • .default() now takes an output-type value and short-circuits parsing. Use .prefault() for the old behaviour, where the default is an input value that gets parsed.
  • z.function() is no longer a schema. It is a factory with .implement()/.implementAsync().
  • The internal ZodEffects class was removed.
  • z.any()/z.unknown() object keys are no longer inferred as optional.

Changes in 4.4 to 4.6 that can reject previously valid input

  • 4.4: z.base64() rejects whitespace, z.httpUrl() rejects https:/host, z.cuid() validation was tightened (CUID v1 deprecated), and .merge() is safer with refinements.
  • 4.5:
    • z.iso.datetime() requires seconds. To also accept HH:MM, use z.union([z.iso.datetime(), z.iso.datetime({ precision: -1 })]).
    • String .min()/.max()/.length() count Unicode code points, not UTF-16 units.
    • Record keys and intersections now follow TypeScript index-signature semantics.
    • __proto__ keys are always stripped.
    • z.ipv6(), z.ulid() and z.httpUrl() are stricter.
  • 4.6:
    • safeParse() builds errors lazily, so error maps and locales run when result.error is first read.
    • z.enum(NumericTsEnum).options no longer includes reverse-mapping keys.
    • z.emoji() rejects component-only strings such as "123".
    • z.toJSONSchema() combines chained bounds correctly; for example, .min(0).max(23).int() now emits 0 and 23.

New APIs worth using

const Fast = z.compile(Player);      // 4.5: precompiled parser, same API, faster
z.validate(z.string(), x);           // 4.5: boolean fast path, type guard
Player.validate(x);                  // 4.6: method form
z.iban(); z.creditCard();            // 4.6 / 4.5 formats
z.deepPartial(S); S.exactPartial();  // 4.5
  • z.withParser() (4.6) installs a pre-generated parser for runtimes without new Function.
  • z.fromJSONSchema() now enforces more keywords.
  • 4.5 reduced schema memory use by about 9x. A recursive-schema memory regression in 4.5 was fixed in 4.6, so prefer >= 4.6.

What to do now

  • Target zod@^4.6 and import * as z from "zod".
  • If a v3 codebase must migrate gradually, import from zod/v3 and convert file by file.
  • Check fixtures that use minute-precision datetimes, emoji-length limits, or string-format edge cases, since 4.5 and 4.6 tightened validation.

Sources