arkynChangelogGuides
docs / bad-responses / unprocessable-entity

UnprocessableEntity

The UnprocessableEntity class represents an HTTP error response with status code 422. It is used to standardize "Unprocessable Entity" error responses, typically for form validation errors where the request syntax is correct but the semantic content is invalid.

Import

ts

import { UnprocessableEntity } from "@arkyn/server/unprocessableEntity";
Learn how subpath and root imports differ in How do I use imports.

Constructor

Unlike other BadResponse classes, UnprocessableEntity takes an object with structured validation data:
  • data (optional): Any additional data related to the error, such as metadata or instructions for the client.
  • fieldErrors (optional): An object mapping field names to error messages, indicating specific validation issues with individual fields.
  • fields (optional): An object containing the original field values that caused the validation errors, allowing clients to repopulate form fields.
  • message (optional): A descriptive message explaining the validation error. Defaults to "Unprocessable entity" if not provided.

Methods

toResponse() - Converts the instance into a Response object with JSON body and Content-Type: application/json header.
toJson() - Alternative method using Response.json() for generating the JSON error response.

Usage example

typescript

import { UnprocessableEntity } from "@arkyn/server/unprocessableEntity";
// Basic form validation error
throw new UnprocessableEntity({
message: "Validation failed",
fieldErrors: {
email: "Invalid email format",
password: "Password must be at least 8 characters",
},
fields: {
email: "invalid-email",
password: "123",
},
});
// With additional data (e.g., scrollTo for auto-scroll to first error)
throw new UnprocessableEntity({
message: "Please fix the form errors",
fieldErrors: { name: "Name is required" },
fields: { name: "" },
data: { scrollTo: "name" },
});
// Convert to Response object
const error = new UnprocessableEntity({
fieldErrors: { username: "Username already taken" },
});
return error.toResponse();

Response structure

The response body includes the validation details:

json

{
"name": "UnprocessableEntity",
"message": "Validation failed",
"cause": {
"data": { "scrollTo": "name" },
"fieldErrors": {
"email": "Invalid email format",
"password": "Password must be at least 8 characters"
},
"fields": {
"email": "invalid-email",
"password": "123"
}
}
}
This is sent with HTTP status 422, set on the Response object itself, the status code is not part of the JSON body. Note that data, fieldErrors, and fields are nested inside cause, not top-level keys. This cause is assigned directly as the object { data, fieldErrors, fields }, so it stays a nested object in the response body. As of v3.0.12, every other BadResponse subclass works the same way, cause is no longer JSON-stringified anywhere, see Breaking Changes.

Integration with SchemaValidator

This class is commonly thrown by SchemaValidator.formValidate() and SchemaValidator.formAsyncValidate():

typescript

import { SchemaValidator } from "@arkyn/server/schemaValidator";
import { z } from "zod";
const schema = z.object({
email: z.string().email("Invalid email"),
name: z.string().min(1, "Name is required"),
});
const validator = new SchemaValidator(schema);
// formValidate throws UnprocessableEntity on validation failure
const data = validator.formValidate(formData, "Please fix the errors");

Notes

When thrown, this class automatically emits a debug log to the console showing the file and function where the error originated. See DebugService to configure ignored files for accurate caller detection.
The fields property preserves the original input values, allowing forms to repopulate fields after a validation error.
Since v3.0.10, fields and data are redacted with parseSensitiveData before being stored on cause, so a value submitted under a sensitive key (password, token, authorization, secret, etc., matched case-insensitively, including nested objects) is masked in the response body instead of being echoed back in clear text. fieldErrors is untouched, it only ever contains messages, not the submitted values. Unlike every other BadResponse subclass, UnprocessableEntity's cause is still included in production responses (NODE_ENV === "production"), it's redacted, structured data meant for the client to repopulate a form, not internal diagnostic detail. See Breaking Changes for the production cause behavior of the other BadResponse classes, and the v3.0.12 entry for the JSON-stringification change that now makes all subclasses consistent.
The data property is passed through as-is, @arkyn/server does not read or act on it in any special way. A key like scrollTo naming the first error field is just a convention some consumers use; interpreting it (e.g. to scroll to the field) is entirely up to your own frontend code, not something the library implements.
Use UnprocessableEntity (422) for semantic validation errors. Use BadRequest (400) for malformed syntax or missing required parameters.
Related in Bad responses
On this page
    arkyn