zod safeparse error messages typescript

Zod safeParse Error Messages in TypeScript

Format Zod safeParse errors into readable TypeScript field errors, API responses, logs, and UI messages without throwing.

Open the JSON to Zod converter

Quick answer: how should safeParse errors be handled?

Use `safeParse` when validation failure is expected and the caller needs a structured response. The result is a discriminated union: success includes parsed data, failure includes a `ZodError` with issues that can be flattened, logged, or mapped into API field messages.

Basic safeParse result

The common pattern is to branch on `result.success`. Once TypeScript sees the success check, it narrows `result.data` and `result.error` correctly. If the schema started in the JSON to Zod converter, keep this handling near the boundary that receives unknown data.

const result = UserSchema.safeParse(payload);

if (!result.success) {
  return {
    ok: false,
    issues: result.error.issues,
  };
}

return {
  ok: true,
  user: result.data,
};

Turn issues into field errors

Use `flatten()` when a form or API client wants errors grouped by field. This is easier to consume than sending the full issues array to every caller.

const result = CreateUserSchema.safeParse(body);

if (!result.success) {
  const fieldErrors = result.error.flatten().fieldErrors;

  return Response.json(
    { error: "Invalid request body", fieldErrors },
    { status: 400 },
  );
}

Preserve path information

Use `error.issues` when nested paths matter. Each issue includes a path, code, and message, which is useful for API logs, table rows, nested form fields, and support debugging.

  • Use `issue.path.join(".")` for readable nested field names.
  • Keep `issue.code` in logs so repeated failures can be grouped.
  • Return concise user-facing messages and keep raw payloads out of public responses.

API response pattern

For route handlers and server actions, safeParse makes validation a normal control-flow branch instead of an exception. Return a 400 response for invalid input and only call business logic with parsed data.

export async function POST(request: Request) {
  const body: unknown = await request.json();
  const parsed = CreateProjectSchema.safeParse(body);

  if (!parsed.success) {
    return Response.json(
      { error: "Validation failed", issues: parsed.error.flatten().fieldErrors },
      { status: 400 },
    );
  }

  return Response.json(await createProject(parsed.data));
}

UI message pattern

Client-side UI usually wants short messages by field. Keep the raw Zod error in developer logs or telemetry and render only the first message for each input when the form is compact.

  • Use field errors for input-level messages.
  • Use form errors for cross-field or object-level failures.
  • Use `safeParseAsync` when refinements or transforms are asynchronous.

Production checklist

Before shipping safeParse error handling, make sure callers get stable error shapes and tests cover invalid, missing, null, and nested values.

  • Do not expose secret raw payloads in validation responses.
  • Test `flatten()` output for the fields your UI or API client expects.
  • Log enough issue metadata to debug contract drift.
  • Keep parse for unexpected invalid upstream data and safeParse for recoverable validation branches.

Use FrameworkKit to generate the starter code, then review the output before shipping it in production.

Generate with the JSON to Zod schema generator

Zod validation resources

JSON to Zod converter

Convert sample payloads into copy-ready Zod schemas and inferred TypeScript types in the browser.

JSON to Zod schema examples

Open realistic API and product payload examples before adapting the JSON to Zod schema output.

TypeScript to Zod Converter

Convert TypeScript interfaces and type aliases into Zod schemas when the source shape already lives in code.

Zod to JSON Schema Converter

Use the canonical online converter when an existing Zod schema needs JSON Schema, AJV, or OpenAPI output.

Validate API responses with Zod

Validate API responses, request bodies, and fetch boundaries with Zod schemas in TypeScript.

Zod parse vs safeParse

Choose between parse, safeParse, parseAsync, and safeParseAsync for TypeScript validation flows.

Zod safeParse

Validate unknown data without throwing, narrow the result type, and format safeParse errors.

Zod refine vs superRefine

Add custom validation, async checks, and field-level error paths after generating a starter schema.

Zod nativeEnum in Zod 4

Migrate z.nativeEnum patterns to z.enum, validate TypeScript enums, and avoid enum value mistakes.

Use JSON to Zod for form validation

Start from a submitted form payload, then add business rules such as email, length, and enum checks.

Zod to JSON Schema for OpenAPI

Publish Zod API schemas as OpenAPI-compatible contracts when teams need portable documentation.

Zod vs JSON Schema

Choose between TypeScript-first runtime validation and portable schema contracts.

Zod vs Yup vs Valibot

Compare TypeScript validation libraries for API boundaries, forms, server actions, and bundle tradeoffs.

FAQ

Does safeParse throw an error?

No. `safeParse` returns an object with `success: true` and parsed data, or `success: false` and a ZodError. Use `parse` when you want invalid data to throw.

How do I get field errors from safeParse?

Call `result.error.flatten().fieldErrors` after checking `result.success === false`. That groups messages by field name for forms and API responses.

Should APIs return error.issues or flattened errors?

Return flattened field errors when clients need a stable field map. Use `error.issues` when nested paths, issue codes, or detailed debugging information matter.

When should I use safeParseAsync?

Use `safeParseAsync` when the schema uses asynchronous refinements, transforms, or validation checks that return promises.

Related comparisons

Related tools