Error handling

Exception handling

The parse method returns the typed object after validation(or any transformations). However if the validation fails, then it will throw a ZodError exception. This exception contains a wealth of information about what went wrong during the validation process. Let's take a closer look at the key properties of a ZodError object:

  • ZodError.message: A concise summary of all validation errors.
  • ZodError.errors: An array of individual error objects, each providing detailed information about the specific validation failure.
  • ZodError.issues: An object that maps paths to arrays of corresponding error objects. This can help you pinpoint where the errors occurred in your data structure.

Here's a simple example illustrating how to handle a ZodError:

import { z } from 'zod';
 
const userSchema = z.object({
  username: z.string().min(3),
  email: z.string().email(),
});
 
const data = {
  username: 'jo',
  email: 'invalid-email',
};
 
try {
  const validUserData = userSchema.parse(data);
  console.log('Valid user data:', validUserData);
} catch (error) {
  if (error instanceof z.ZodError) {
    // Handle the ZodError
    console.error('Validation errors:', error.errors);
  } else {
    // Handle other types of errors
    console.error('An unexpected error occurred:', error);
  }
}

SafeParse Method

Unlike the standard parse method, which throws a ZodError exception upon validation failure, safeParse provides a non-throwing alternative that returns a result object containing information about the validation outcome. The object contains the two following properties:

  • success: A boolean indicating whether the validation was successful (true) or not (false).
  • data: The successfully parsed and validated data, provided success is true.
  • error: The ZodError instance.

Here's a basic example of how to use safeParse:

import { z } from 'zod';
 
const userSchema = z.object({
  username: z.string().min(3),
  email: z.string().email(),
});
 
const data = {
  username: 'jo',
  email: 'invalid-email',
};
 
const result = userSchema.safeParse(data);
 
if (result.success) {
  console.log('Valid user data:', result.data);
} else {
  console.error('Validation errors:', result.error);
}

Error Messages

Error messages are a fundamental component of any application's user experience. They not only help developers debug issues but also provide essential feedback to users when something goes wrong. We can add custom error messages to any validation method in Zod.

Lets take a look at an example:

import { z } from 'zod';
 
const ageSchema = z.number().min(18, { message: 'You must be 18 years or older to proceed.' });
 
const userInput = 15;
 
const validationResult = ageSchema.safeParse(userInput);
 
if (validationResult.success) {
  console.log('User is eligible:', validationResult.data);
} else {
  console.error('Validation error:', validationResult.error.message);
}

Its more common usage is with the refine method. If we recall, refine is used to add custom validation logic. It makes sense to also provide a custom error message along with it too.

Error Map

We can also define an error maps of sorts. The idea is simple. Given the error, return a custom message for it. The error map can then be used while calling safeParse or parse. Heres an example taken right from the documentation:

The Error object here is a discriminated union based off its code property.

import * as z from "zod";
 
const customErrorMap: z.ZodErrorMap = (error, ctx) => {
  /*
  This is where you override the various error codes
  */
  switch (error.code) {
    case z.ZodIssueCode.invalid_type:
      if (error.expected === "string") {
        return { message: `This ain't a string!` };
      }
      break;
    case z.ZodIssueCode.custom:
      // produce a custom message using error.params
      // error.params won't be set unless you passed
      // a `params` arguments into a custom validator
      const params = error.params || {};
      if (params.myField) {
        return { message: `Bad input: ${params.myField}` };
      }
      break;
  }
 
  // fall back to default message!
  return { message: ctx.defaultError };
};
 
z.string().parse(12, { errorMap: customErrorMap });