@next-safe-action/safe-action-validation-errors

Use when working with validation errors -- returnValidationErrors, formatted vs flattened shapes, custom validation error shapes, throwValidationErrors, or displaying field-level and form-level errors

View in AI SkillSafe app
Scanned · no findings
0 downloads
0 stars
0 demos
SKILL.md
namesafe-action-validation-errors
descriptionUse when working with validation errors -- returnValidationErrors, formatted vs flattened shapes, custom validation error shapes, throwValidationErrors, or displaying field-level and form-level errors

next-safe-action Validation Errors

Two Sources of Validation Errors

  1. Schema validation — automatic when input doesn't match .inputSchema()
  2. Manual validation — via returnValidationErrors() in server code (e.g., "email already taken")

Both produce the same error structure on the client.

Default Error Shape (Formatted)

Mirrors the schema structure with _errors arrays at each level:

// For schema: z.object({ email: z.string().email(), address: z.object({ city: z.string() }) })
{
  _errors: ["Form-level error"],                    // root errors
  email: { _errors: ["Invalid email address"] },    // field errors
  address: {
    _errors: ["Address section error"],
    city: { _errors: ["City is required"] },        // nested field errors
  },
}

returnValidationErrors

Throws a ActionServerValidationError that the framework catches and returns as result.validationErrors. It never returns — it always throws.

For expected non-validation errors ("out of stock", "not found"), the sibling helper returnServerError(payload) works the same way (throws internally, never returns) but sets result.serverError to the typed payload, bypassing handleServerError. See the safe-action-client skill.

"use server";

import { z } from "zod";
import { returnValidationErrors } from "next-safe-action";
import { actionClient } from "@/lib/safe-action";

const registerSchema = z.object({
  email: z.string().email(),
  username: z.string().min(3),
});

export const register = actionClient
  .inputSchema(registerSchema)
  .action(async ({ parsedInput }) => {
    // Check business rules after schema validation passes
    const existingUser = await db.user.findByEmail(parsedInput.email);
    if (existingUser) {
      returnValidationErrors(registerSchema, {
        email: { _errors: ["This email is already registered"] },
      });
    }

    const existingUsername = await db.user.findByUsername(parsedInput.username);
    if (existingUsername) {
      returnValidationErrors(registerSchema, {
        username: { _errors: ["This username is taken"] },
      });
    }

    // Both checks passed — create the user
    const user = await db.user.create(parsedInput);
    return { id: user.id };
  });

Root-Level Errors

Use _errors at the top level for form-wide errors:

returnValidationErrors(schema, {
  _errors: ["You can only create 5 posts per day"],
});

Supporting Docs

Displaying Validation Errors

// Formatted shape (default)
{result.validationErrors?.email?._errors?.map((error) => (
  <p key={error} className="text-red-500">{error}</p>
))}

// Root-level errors
{result.validationErrors?._errors?.map((error) => (
  <p key={error} className="text-red-500">{error}</p>
))}
// Flattened shape
{result.validationErrors?.fieldErrors?.email?.map((error) => (
  <p key={error} className="text-red-500">{error}</p>
))}

// Form-level errors (flattened)
{result.validationErrors?.formErrors?.map((error) => (
  <p key={error} className="text-red-500">{error}</p>
))}

Embed badges

Add these to your README to show the skill's verification status.

SkillSafe verified badge
Verified badge
[![SkillSafe verified badge](https://api.skillsafe.ai/v1/badge/@next-safe-action/safe-action-validation-errors/verified)](https://skillsafe.ai/skill/@next-safe-action/safe-action-validation-errors/)
Installs badge
Installs badge
[![Installs badge](https://api.skillsafe.ai/v1/badge/@next-safe-action/safe-action-validation-errors/installs)](https://skillsafe.ai/skill/@next-safe-action/safe-action-validation-errors/)
Scan badge
Scan badge
[![Scan badge](https://api.skillsafe.ai/v1/badge/@next-safe-action/safe-action-validation-errors/scan)](https://skillsafe.ai/skill/@next-safe-action/safe-action-validation-errors/)
Eval pass rate badge
Eval pass rate
[![Eval pass rate badge](https://api.skillsafe.ai/v1/badge/@next-safe-action/safe-action-validation-errors/eval)](https://skillsafe.ai/skill/@next-safe-action/safe-action-validation-errors/)