Zod logo

Customizing errors

In Zod, validation errors are surfaced as instances of the z.core.$ZodError class.

The zod package uses a subclass of this called ZodError that implements some additional convenience methods.

Instances of $ZodError contain an .issues property containing a human-readable message and additional structured information about each encountered validation issue.

import * as z from "zod";
 
const result = z.string().safeParse(12); // { success: false, error: ZodError }
result.error.issues;
// [
//   {
//     expected: 'string',
//     code: 'invalid_type',
//     path: [],
//     message: 'Invalid input: expected string, received number'
//   }
// ]

Every issue inside a $ZodError contains a message property with a human-readable error message. This message can be customized in a number of ways.

The error param

Virtually every Zod API accepts an optional error message parameter.

z.string("Not a string!");

This custom error will show up as the message property of any validation issues that originate from this schema.

z.string("Not a string!").parse(12);
// ❌ throws ZodError {
//   issues: [
//     {
//       expected: 'string',
//       code: 'invalid_type',
//       path: [],
//       message: 'Not a string!'   <-- 👀 custom error message 
//     }
//   ]
// }

All z functions and schema methods accept custom errors.

z.string("Bad!");
z.string().min(5, "Too short!");
z.uuid("Bad UUID!");
z.iso.date("Bad date!");
z.array(z.string(), "Bad array!");
z.array(z.string()).min(5, "Too few items!");
z.set(z.string(), "Bad set!");

If you prefer, you can pass a params object with an error parameter instead.

z.string({ error: "Bad!" });
z.string().min(5, { error: "Too short!" });
z.uuid({ error: "Bad UUID!" });
z.iso.date({ error: "Bad date!" });
z.array(z.string(), { error: "Bad array!" });
z.array(z.string()).min(5, { error: "Too few items!" });
z.set(z.string(), { error: "Bad set!" });

The error param optionally accepts a function. This function will be called at parse time if a valiation error occurs.

z.string({ error: ()=>`[${Date.now()}]: Validation failure.` });

Note — In Zod v3, there were separate params for message (a string) and errorMap (a function). These have been unified in Zod 4 as error.

The error function received a context object you can use to customize the error message based on the input or other validation information.

z.string({
  error: (iss) => iss.input===undefined ? "Field is required." : "Invalid input."
});

For advanced cases, the iss object provides additional information you can use to customize the error.

z.string({
  error: (iss) => {
    iss.code; // the issue code
    iss.input; // the input data
    iss.inst; // the schema/check that originated this issue
    iss.path; // the path of the error
  },
});

Depending on the API you are using, there may be additional properties available. Use TypeScript's autocomplete to explore the available properties.

z.string().min(5, {
  error: (iss) => {
    // ...the same as above
    iss.minimum; // the minimum value
    iss.inclusive; // whether the minimum is inclusive
    return `Password must have ${iss.minimum} characters or more`;
  },
});

Per-parse error customization

To customize errors on a per-parse basis, pass an error map into the parse method:

const schema = z.string()
 
schema.parse(12, {
  error: iss => "per-parse custom error"
};

This has lower precedence than any schema-level custom messages.

const schema = z.string({ error: "highest priority" });
const result = schema.safeParse(12, {
  error: (iss) => "lower priority",
})
 
result.error.issues;
// [{ message: "highest priority", ... }]

The iss object is a discriminated union of all possible issue types. Use the code property to discriminate between them.

For a breakdown of all Zod issue codes, see the @zod/core documentation.

const result = schema.safeParse(12, {
  error: (iss) => {
    if (iss.code === "invalid_type") {
      return `invalid type, expected ${iss.expected}`;
    }
    if (iss.code === "too_small") {
      return `minimum is ${iss.minimum}`;
    }
    // ...
  }
})

Global error customization

To specify a global error map, use z.config() to set Zod's customError configuration setting:

z.config({
  customError: (iss) => {
    return "globally modified error";
  },
});

Global error messages have lower precedence than schema-level or per-parse error messages.

The iss object is a discriminated union of all possible issue types. Use the code property to discriminate between them.

For a breakdown of all Zod issue codes, see the @zod/core documentation.

const result = schema.safeParse(12, {
  error: (iss) => {
    if (iss.code === "invalid_type") {
      return `invalid type, expected ${iss.expected}`;
    }
    if (iss.code === "too_small") {
      return `minimum is ${iss.minimum}`;
    }
    // ...
  }
})

Internationalization

To support internationalization of error message, Zod provides several built-in locales. These are exported from the @zod/core package.

Note — The zod library automatically loads the en locale automatically. The @zod/minipackage does not load any locale; instead all error messages default to Invalid input.

import * as z from "zod";
import en from "@zod/core/locales/en"
 
z.config(en());

To lazily load a locale, consider dynamic imports:

import * as z from "zod";
 
async function loadLocale (locale: string) {
  const { default: locale } = await import(`@zod/core/locales/${locale}`);
  z.config(locale());
};
 
await loadLocale("fr");

For convenience, all locales are exported as z.locales feom zod/@zod/mini.

import * as z from "zod";
 
z.config(z.locales.en());

Locales

The following locales are available:

  • az — Azerbaijani
  • en — English

Error precedence

Below is a quick reference for determining error precedence: if multiple error customizations have been defined, which one takes priority? From highest to lowest priority:

  1. Schema-level error — Any error message "hard coded" into a schema definition.
z.string("Not a string!");
  1. Per-parse error — A custom error map passed into the .parse() method.
z.string().parse(12, {
  error: (iss) => "My custom error"
});
  1. Global error map — A custom error map passed into z.config().
z.config({
  customError: (iss) => "My custom error"
});
  1. Locale error map — A custom error map passed into z.config().
z.config(z.locales.en());

On this page