Blog
Wild & Free Tools

Zod safeParse vs parse: Which to Use and When

Last updated: April 2026 5 min read

Table of Contents

  1. parse(): Throws on Failure
  2. safeParse(): Returns a Result Object
  3. When to Use Each
  4. Working with safeParse Error Output
  5. Frequently Asked Questions

Zod gives you two methods to validate data: parse() throws a ZodError if validation fails, while safeParse() returns a result object with { success: true, data } or { success: false, error }. Use safeParse() in most application code — it avoids try/catch blocks and gives you structured error handling. Use parse() when you want the function to throw and let the caller handle exceptions.

This page covers both methods with code examples and the exact situations where each fits.

parse(): Throws on Failure

parse() returns the validated data if successful, or throws a ZodError if not:

import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email()
});

// Success case
const user = UserSchema.parse({ id: 1, name: 'Alice', email: '[email protected]' });
// user is typed as { id: number; name: string; email: string }

// Failure case — throws ZodError
try {
  const user = UserSchema.parse({ id: 'not-a-number', name: 'Alice' });
} catch (err) {
  if (err instanceof ZodError) {
    console.log(err.issues); // array of validation issues
  }
}

The ZodError contains an issues array with one entry per failed field, each with a path, message, and code.

safeParse(): Returns a Result Object

safeParse() never throws — it wraps the result in a discriminated union:

const result = UserSchema.safeParse(data);

if (result.success) {
  // result.data is typed as the schema type
  console.log(result.data.name);
} else {
  // result.error is a ZodError
  result.error.issues.forEach(issue => {
    console.log(issue.path.join('.'), issue.message);
  });
}

The result.success check is a TypeScript discriminant — after the if branch, TypeScript knows exactly what result contains. No type narrowing needed.

Sell Custom Apparel — We Handle Printing & Free Shipping

When to Use Each

SituationUse
API route handler — return 400 on invalid inputsafeParse()
Service function — caller handles errorsparse()
Express/Fastify middlewaresafeParse()
tRPC procedure inputtRPC calls parse() internally
React Hook Form (via zodResolver)zodResolver calls safeParse() internally
Validating config at app startupparse() — crash fast if config is wrong
Validating user-submitted datasafeParse() — return errors to user
Unit tests asserting valid dataparse() — test failure shows the ZodError

Working with safeParse Error Output

Three ways to use the error from a failed safeParse():

1. issues array — flat list of all errors:

result.error.issues
// [ { path: ['email'], message: 'Invalid email', code: 'invalid_string' } ]

2. flatten().fieldErrors — object keyed by field name (best for forms):

result.error.flatten().fieldErrors
// { email: ['Invalid email'], id: ['Expected number, received string'] }

3. flatten().formErrors — top-level errors not tied to a specific field:

result.error.flatten().formErrors
// ['Passwords do not match'] (from .refine() on the whole object)

For API responses, return flatten().fieldErrors. For logging, use issues directly. To generate a starting schema for what you're validating, use the JSON to Zod converter.

Try It Free — No Signup Required

Runs 100% in your browser. No data is collected, stored, or sent anywhere.

Open Free JSON to Zod Converter

Frequently Asked Questions

What is the difference between Zod parse and safeParse?

parse() throws a ZodError if validation fails. safeParse() returns { success: true, data } or { success: false, error } without throwing. Use safeParse() in most application code to avoid try/catch blocks.

When should I use parse() instead of safeParse()?

Use parse() when you want the error to propagate as an exception — for example, validating app config at startup (crash fast), in service functions where the caller handles errors, or in unit tests where you want the ZodError to surface as a test failure.

How do I get field-level error messages from safeParse?

Call result.error.flatten().fieldErrors on the error from a failed safeParse. It returns an object keyed by field name, with an array of error messages for each field. Ideal for returning validation errors to client-side forms.

Does safeParse return undefined if validation fails?

No. safeParse always returns an object. On failure: { success: false, error: ZodError }. The data property does not exist on the failure case. On success: { success: true, data: T }. The error property does not exist on the success case.

Launch Your Own Clothing Brand — No Inventory, No Risk