Validation utilities
next-safe-action exports several utility functions and constants for working with validation errors.
All utilities are imported from the main entry point:
import {
returnValidationErrors,
returnServerError,
flattenValidationErrors,
formatValidationErrors,
DEFAULT_SERVER_ERROR_MESSAGE,
} from "next-safe-action";returnValidationErrors()
Throw validation errors from server action code. This function never returns. It throws internally and the error is caught by the action builder and returned as validationErrors in the result.
function returnValidationErrors<S extends StandardSchemaV1>(
schema: S,
validationErrors: ValidationErrors<S>
): never;Parameters:
Prop
Type
Returns: never, this function always throws.
export const signUp = actionClient
.inputSchema(z.object({
email: z.string().email(),
username: z.string().min(3),
}))
.action(async ({ parsedInput }) => {
const emailExists = await db.user.findByEmail(parsedInput.email);
if (emailExists) {
// Throws internally, code below this line never executes
return returnValidationErrors(signUpSchema, {
email: { _errors: ["Email already registered"] },
});
}
// Form-level errors (not tied to a specific field)
return returnValidationErrors(signUpSchema, {
_errors: ["Registration is currently disabled"],
});
});returnValidationErrors accepts both direct schemas and async schema factory functions. When using an async factory (for i18n), pass the factory function itself.
returnServerError()
Return a typed, expected server error to the client from server action code or middleware. This function never returns. It throws internally and the error is caught by the action builder and returned as serverError in the result, bypassing handleServerError.
function returnServerError<SE>(serverError: SE): never;Parameters:
Prop
Type
Returns: never, this function always throws.
export const buyProduct = actionClient
.inputSchema(z.object({ productId: z.string() }))
.action(async ({ parsedInput }) => {
const product = await db.product.find(parsedInput.productId);
if (!product.inStock) {
// Throws internally, code below this line never executes
returnServerError({ code: "OUT_OF_STOCK", message: "This product is sold out" });
}
// ...
});Since SE is inferred from the argument, the payload isn't checked against the client's ServerError type by default. Enforce it at the call site with an explicit generic or satisfies, or export a typed alias next to your action client:
returnServerError<AppServerError>({ code: "OUT_OF_STOCK", message: "This product is sold out" });
// or
returnServerError({ code: "OUT_OF_STOCK", message: "This product is sold out" } satisfies AppServerError);
// or, recommended: a typed alias in lib/safe-action.ts
export const returnAppError: (e: AppServerError) => never = returnServerError;See Typing the error payload for the full explanation.
The payload is JSON-encoded onto the error digest, so it also works when thrown inside a Next.js "use cache" scope with cacheComponents enabled. See Error handling for more details.
flattenValidationErrors()
Transform formatted validation errors into a flat structure with formErrors and fieldErrors. Discards errors for nested fields (only keeps one level deep).
function flattenValidationErrors<VE extends ValidationErrors<any>>(
validationErrors: VE
): FlattenedValidationErrors<VE>;Parameters:
Prop
Type
Returns: FlattenedValidationErrors<VE>, an object with formErrors: string[] and fieldErrors: { [key]: string[] }.
// Input (formatted shape):
{
_errors: ["Passwords don't match"],
name: { _errors: ["Too short"] },
address: { street: { _errors: ["Required"] } },
}
// Output (flattened shape):
{
formErrors: ["Passwords don't match"],
fieldErrors: {
name: ["Too short"],
// address.street is discarded (nested)
},
}Usage with handleValidationErrorsShape
import { flattenValidationErrors } from "next-safe-action";
export const myAction = actionClient
.inputSchema(schema, {
handleValidationErrorsShape: (ve) => flattenValidationErrors(ve),
})
.action(async ({ parsedInput }) => { /* ... */ });formatValidationErrors()
Identity function that returns the formatted validation errors as-is. Useful as a named reference when you want to be explicit about using the default shape.
function formatValidationErrors<VE extends ValidationErrors<any>>(
validationErrors: VE
): VE;DEFAULT_SERVER_ERROR_MESSAGE
The default error message returned to the client when a server error occurs and no custom handleServerError is provided.
const DEFAULT_SERVER_ERROR_MESSAGE: string;
// Value: "Something went wrong while executing the operation."This constant is useful when you want to check if the client received the default error message, or when building custom error handlers that should fall back to the default.
import { DEFAULT_SERVER_ERROR_MESSAGE } from "next-safe-action";
const actionClient = createSafeActionClient({
handleServerError: (error) => {
if (error instanceof AuthError) {
return "Authentication failed";
}
return DEFAULT_SERVER_ERROR_MESSAGE;
},
});See also
- Custom validation errors: guide for customizing error shapes
- Error handling: the complete error taxonomy
- Error classes: all exported error classes