ZodError structure and issues array
When .parse() validation fails, it throws a ZodError instance. The error has an .issues property containing an array of error objects. Each error object contains: expected (the expected type), code (the error code like 'invalid_type'), path (array showing the location of the error), and message (human-readable error message).
ZodError instanceof check for Zod full build
To check for a ZodError in the full Zod build, use 'error instanceof z.ZodError'.
ZodError instanceof check for Zod mini build
To check for a ZodError in the Zod mini build, use 'error instanceof z.core.$ZodError'.
Zod's error map system will be simplified in Zod 4
To improve error reporting, Zod's error map system will be simplified in Zod 4. The new system will also be more amenable to internationalization.
Refinements execute in both encode and decode directions
All checks (.refine(), .min(), .max(), etc.) are executed in both directions during encode and decode operations. Zod performs two passes during z.encode(): first pass ensures input type conforms to expected type, second pass executes refinement logic.
ZodError class and issues array structure
In Zod, validation errors are instances of the z.core.$ZodError class. The ZodError class in the zod package is a subclass that implements additional convenience methods. Instances contain an .issues array where each issue has a human-readable message property and additional structured metadata about the issue.
Error parameter accepts string or function
Every Zod API accepts an optional error message parameter. The error parameter can be a string (e.g., z.string('Not a string!')) or a function known as an error map that receives a context object and runs at parse time. Passing undefined from an error map defers to the next error map in the precedence chain.
Error map context object properties
An error map function receives an iss object with the following properties: code (the issue code), input (the input data), inst (the schema/check that originated the issue), and path (the path of the error). Depending on the API, additional properties may be available such as iss.minimum and iss.inclusive for min validations.
Per-parse error customization has lower precedence than schema-level
To customize errors on a per-parse basis, pass an error map into the parse method: schema.parse(12, { error: iss => 'per-parse custom error' }). Per-parse error customization has lower precedence than schema-level custom messages, so schema-level errors will override per-parse errors.
Issue code as discriminated union
The iss object passed to error maps is a discriminated union of all possible issue types. Use the code property to discriminate between them. For example, check if iss.code === 'invalid_type' or iss.code === 'too_small'.
reportInput flag includes input data in issues
By default, Zod does not include input data in issues to prevent unintentional logging of sensitive data. To include input data in each issue, use the reportInput flag: z.string().parse(12, { reportInput: true }). This adds the input property to each error issue.
Global error customization with z.config()
To specify a global error map, use z.config() with a customError setting: z.config({ customError: (iss) => { return 'globally modified error'; } }). Global error messages have lower precedence than schema-level or per-parse error messages and apply to all subsequent validations.
Locale support and loading
Zod provides built-in locales exported from zod/v4/core package. The regular zod library loads the en locale automatically, while Zod Mini does not load any locale by default and all error messages default to 'Invalid input'. Locales can be configured with z.config(en()) or dynamically imported with async/await.
Available locale codes
Zod supports the following 45 locale codes: ar, az, be, bg, ca, cs, da, de, en, eo, es, fa, fi, fr, frCA, he, hu, hy, id, is, it, ja, ka, km, ko, lt, mk, ms, nl, no, ota, ps, pl, pt, ro, ru, sl, sv, ta, th, tr, uk, ur, uz, vi, zhCN, zhTW, yo.
Error precedence from highest to lowest
Error precedence order (highest to lowest): 1) Schema-level error (hard-coded into schema definition), 2) Per-parse error (passed into .parse() method), 3) Global error map (passed into z.config()), 4) Locale error map (from z.config(z.locales.en())). Higher precedence errors override lower precedence ones.
z.treeifyError() nested structure format
z.treeifyError() converts a ZodError into a nested object that mirrors the schema structure. The result has an 'errors' field containing error messages at a given path, and special properties 'properties' and 'items' for traversing deeper into the tree. For array validation errors, 'items' is an array where undefined represents valid indices and objects with 'errors' fields represent invalid indices.
z.treeifyError() example with nested access
const tree = z.treeifyError(result.error);
// Access nested errors using optional chaining:
tree.properties?.username?.errors;
// => ["Invalid input: expected string, received number"]
tree.properties?.favoriteNumbers?.items?.[1]?.errors;
// => ["Invalid input: expected number, received string"]
z.prettifyError() human-readable output
z.prettifyError() provides a human-readable string representation of a ZodError. Each error is prefixed with ✖ and includes the error message. Errors at nested paths show the path using arrow notation and array bracket notation, for example 'at username' or 'at favoriteNumbers[1]'.
z.prettifyError() example output
const pretty = z.prettifyError(result.error);
// Returns:
// ✖ Unrecognized key: "extraKey"
// ✖ Invalid input: expected string, received number
// → at username
// ✖ Invalid input: expected number, received string
// → at favoriteNumbers[1]
z.formatError() deprecated
z.formatError() is deprecated in Zod. Use z.treeifyError() instead for converting errors to nested objects.
z.flattenError() for flat schemas
z.flattenError() converts a ZodError to a shallow, flat error object useful for simple one-level-deep schemas. It returns an object with 'formErrors' array (containing top-level errors where path is []) and 'fieldErrors' object mapping field names to arrays of error messages for each field.
z.flattenError() example output
const flattened = z.flattenError(result.error);
// Returns:
{
formErrors: [ 'Unrecognized key: "extraKey"' ],
fieldErrors: {
username: [ 'Invalid input: expected string, received number' ],
favoriteNumbers: [ 'Invalid input: expected number, received string' ]
}
}
// Access field errors:
flattened.fieldErrors.username; // => [ 'Invalid input: expected string, received number' ]
ZodError issues array structure
A ZodError contains an 'issues' array where each issue object includes: code (e.g., 'invalid_type', 'unrecognized_keys'), expected (the expected type), path (array showing location in schema), message (human-readable error message), and additional type-specific fields like 'keys' for unrecognized_keys errors.
Optional chaining required for nested error access
When accessing deeply nested errors in formatted error objects, always use optional chaining (?.) to avoid runtime errors when intermediate properties are undefined.
$ZodError base class does not extend Error
$ZodError is the base class for all errors in Zod. For performance reasons, $ZodError does not extend the built-in Error class, so using instanceof Error will return false. The $ZodError class implements the Error interface and contains an issues property with an array of $ZodIssue objects.
$ZodIssueBase interface structure
All Zod issues extend the $ZodIssueBase interface with the following properties: code (optional string), input (optional unknown), path (required PropertyKey array), and message (required string).
$ZodIssue subtypes
zod/v4/core defines the following $ZodIssue subtypes: $ZodIssueInvalidType, $ZodIssueTooBig, $ZodIssueTooSmall, $ZodIssueInvalidStringFormat, $ZodIssueNotMultipleOf, $ZodIssueUnrecognizedKeys, $ZodIssueInvalidUnion, $ZodIssueInvalidKey, $ZodIssueInvalidElement, $ZodIssueInvalidValue, and $ZodIssueCustom.
catch() returns fallback on validation error
Use .catch() to define a fallback value returned in case of validation error. Example: z.number().catch(42) will return 42 if parsing fails. Can also accept a function: z.number().catch((ctx) => { ctx.error; return Math.random(); }) which receives the caught ZodError.
catch() function receives context parameter
When .catch() receives a function, it gets ctx parameter with ctx.error (the ZodError). In Zod Mini, the function receives ctx with ctx.value (input value) and ctx.issues (validation issues).
z.prettifyError() formats ZodError as user-friendly string
Call z.prettifyError(zodError) to convert a ZodError to a formatted multi-line string with ✖ symbols and path indicators. Example output: '✖ Unrecognized key: "extraField"\n✖ Invalid input: expected string, received number\n → at username'