TypeScript-first schema validation with static type inference - version-aware skill for Zod v3 and v4
75
94%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Advisory
Suggest reviewing before use
Verify the ask CLI is available (which ask). It is the primary tool for reading the exact version installed in this project — it resolves the version from the lockfile, fetches docs/source once, and caches them at ~/.ask/. If ask is not installed, fall back to node_modules/zod/ and the official site at https://zod.dev (which tracks the latest published v4, not necessarily the installed version).
Before writing Zod code, verify the installed version and entry points:
# installed version — drives everything below
cat node_modules/zod/package.json 2>/dev/null | jq -r .version
# subpath exports — confirms which import paths resolve (zod, zod/mini, zod/v3, zod/v4)
cat node_modules/zod/package.json 2>/dev/null | jq '.exports | keys'If zod is missing, install only what the task requires:
# v4 (current default since zod@4.0.0)
pnpm add zod # or: bun add zod / npm i zod / yarn add zod
# pin to v3 only when the project explicitly requires it
pnpm add zod@^3Detect the package manager from the lockfile (pnpm-lock.yaml → pnpm, bun.lockb → bun, package-lock.json → npm, yarn.lock → yarn).
Zod 4 (released 2025) was a major rewrite. Many APIs that were canonical in v3 are now deprecated, renamed, or removed. Examples that are commonly miswritten from training data:
err.format() / err.flatten() (v3 instance methods) — in v4 these are top-level functions: z.treeifyError(err) / z.flattenError(err). z.formatError() exists but is deprecated in favour of z.treeifyError().z.string({ message, errorMap }) (v3) — v4 unifies these into a single error param: z.string({ error: "Bad!" }) or z.string({ error: (iss) => "..." })..superRefine() is still the recommended v4 API for multi-issue refinements. .check() exists as a lower-level, more verbose alternative for performance-sensitive paths — not as a replacement.error instanceof z.ZodError — works for the regular zod package; for zod/mini use error instanceof z.core.$ZodError (the parent class).z.codec(...)) — only exist in zod@4.1+. Do not suggest them on v3 or earlier 4.x.When working with Zod:
ask (see Finding Documentation below)..d.ts before generating code. Never invent method names.references/versions.md has the v4.3.6 / v3.25.76 links) — not main, which tracks the latest release..superRefine is recommended in v4; .check() is a lower-level alternative for performance-sensitive paths — clarify when relevant).If documentation cannot be found locally or remotely to back an answer, say so explicitly.
Resolve the source checkout and docs directory once with ask; reuse the paths across reads:
SRC=$(ask src zod) # checkout root
DOCS=$(ask docs zod | head -n1) # candidate docs dirBoth pin to the version in the project's lockfile. To inspect a specific version regardless of the project, append @version:
SRC_V4=$(ask src zod@4.3.6)
SRC_V3=$(ask src zod@3.25.76)cat "$DOCS/README.md"
ls "$SRC/packages/docs/content" # v4 docs source (mdx)
cat "$SRC/packages/docs/content/api.mdx" # full API reference
cat "$SRC/packages/docs/content/error-formatting.mdx"
cat "$SRC/packages/docs/content/error-customization.mdx"
cat "$SRC/packages/docs/content/codecs.mdx" # v4.1+ only# top-level functions (v4): treeifyError, prettifyError, flattenError, codec, config
rg -n "^export (function|const) (treeifyError|prettifyError|flattenError|codec|config)\\b" "$SRC/packages/zod/src"
# instance methods on schemas
rg -n "(\\.refine|\\.check|\\.superRefine|\\.overwrite|\\.transform|\\.parseAsync)\\b" "$SRC/packages/zod/src"
# subpath exports
cat "$SRC/packages/zod/package.json" | jq '.exports | keys'fd -e test.ts . "$SRC/packages/zod/tests"
rg -n "discriminatedUnion|z\\.codec|treeifyError" "$SRC/packages/zod/tests"ask is unavailableSRC=./node_modules/zod
ls $SRC/dist
rg "treeifyError" $SRC/dist # confirm v4 helpers shipped in this build
cat $SRC/package.json | jq .versionUse https://zod.dev only to cross-reference — it always tracks the latest published v4.
node -e "const v=require('zod/package.json').version; console.log(v.startsWith('4.')?'v4':v.startsWith('3.')?'v3':v)"| Detected | Default import | Errors API | Refinement API | Codecs |
|---|---|---|---|---|
| v4 (≥4.0.0) | import * as z from "zod" | z.treeifyError, z.prettifyError, z.flattenError | .refine(), .superRefine(), .check() (low-level) | z.codec() (4.1+) |
| v3 (≥3.0, <4.0) | import { z } from "zod" | err.format(), err.flatten() | .refine(), .superRefine() | — |
| v3.25.x bridge | import * as z from "zod/v4" opt-in to v4 alongside v3 | per the chosen path | per the chosen path | — |
zod@3.25 shipped both v3 (default) and v4 (under zod/v4) in the same package to ease migration. From zod@4.0.0 onward, the root export is v4 and zod/v3 is the back-compat path. See references/versions.md.
| Import | Use when |
|---|---|
import * as z from "zod" | Default. Standard ergonomic API with chainable methods (z.string().min(5)). |
import * as z from "zod/mini" | Bundle-size-sensitive frontend code. Functional API: z.string().check(z.minLength(5)). ~64% smaller for trivial schemas. |
import * as z from "zod/v3" | Legacy code on v3 that you can't migrate yet, while consuming a zod@4 package. |
import * as z from "zod/v4-mini" (within zod@3.25) | Forward-compat path for projects pinned to v3 that want to start adopting Mini. |
zod/mini and zod interop: schemas from one cannot be passed to the other's parse functions. Pick one per project unless you have a deliberate reason to mix.
Concise cookbook of common patterns, each tagged with the version it applies to: references/schemas.md.
Rules of thumb:
z.object is non-strict by default — extra keys are stripped. Use z.strictObject({...}) to reject extra keys, or .passthrough() (v3) / .loose() (v4) to preserve them..optional() vs .nullable() vs .nullish() — optional allows undefined, nullable allows null, nullish allows both.z.infer<typeof Schema>. Use z.input and z.output separately when the schema transforms (input ≠ output, e.g. z.string().transform(s => s.length)).z.discriminatedUnion("type", [...]) is dramatically faster and produces better error messages than z.union(...) when shapes share a tag field.get children() { return z.array(Self); }); v3 uses z.lazy(() => Schema) plus an explicit z.ZodType<Node> annotation. See references/schemas.md.Concise reference: references/parsing-and-errors.md.
Quick map:
parse(input) — throws on invalid; returns typed deep clone.safeParse(input) — returns { success: true, data } | { success: false, error: ZodError }.parseAsync / safeParseAsync — required when the schema contains async refinements, transforms, or codecs.try { ... } catch (e) { if (e instanceof z.ZodError) e.issues } — every error has an .issues array of { code, path, message, expected?, ... }.The formatError → treeifyError rename is the single most common source of broken v3-era examples. Surface it whenever rewriting v3 error-handling code.
Before searching source code, check the most common Zod failure modes:
Invalid input: expected X with no path — top-level shape mismatch; verify the schema matches the expected outer type.Cannot read property 'parseAsync' of undefined — usually an import-path mismatch (zod vs zod/mini); methods on Mini schemas live on top-level functions instead.Type 'ZodError' is not assignable to type '$ZodError' — mixing zod and zod/mini schemas in the same code path.parse to parseAsync (or safeParse to safeParseAsync).error param (v4) and not the legacy message/errorMap shape (v3).If the symptom is not listed, resolve the source and grep the error string:
rg -n "error string fragment" "$(ask src zod)/packages/zod/src"
# fallback: rg -n "error string fragment" node_modules/zod/distreferences/versions.md — entry points, version detection, v3 ↔ v4 API rename cheatsheet, links to upstream docs at version pins (v4.3.6, v3.25.76)references/schemas.md — primitives, objects, arrays, unions, discriminated unions, recursion, refinements, transforms, codecs (v4.1+) — each example tagged // v4, // v3, or // bothreferences/parsing-and-errors.md — parse vs safeParse vs parseAsync, ZodError shape, treeifyError/prettifyError/flattenError (v4), format/flatten (v3), error customization