SiteError.comYour friendly guide to HTTP status codes
Status CodesBlog
  1. Home
  2. Blog
  3. Understanding HTTP 422 Unprocessable Entity: Validation Errors Done Right

Understanding HTTP 422 Unprocessable Entity: Validation Errors Done Right

July 20, 202612 min read
4xxClient Error

422 Unprocessable Entity is the status code for "I understood you perfectly, and the answer is still no." The request parsed fine, the JSON was valid, every header was in order — but the content doesn't make sense: an email address that isn't one, a meeting scheduled for February 30th, a quantity of -3. The catch is that the line between 400 and 422 is genuinely blurry, and plenty of respectable APIs just throw 400 at everything. This post covers what 422 actually means, where the 400/422 boundary really sits, the rename to "Unprocessable Content" in RFC 9110, and how to return validation errors that clients can actually act on.

What Is a 422?

A 422 means the request was well-formed but couldn't be followed due to semantic errors — the server read it, understood it, and rejected what it said.

The 422 (Unprocessable Entity) status code means the server understands the content type of the request entity (hence a 415 Unsupported Media Type status code is inappropriate), and the syntax of the request entity is correct (thus a 400 Bad Request status code is inappropriate) but was unable to process the contained instructions. — RFC 4918, Section 11.2

In plain English: "Your request is syntactically correct, but it has semantic or validation errors." A properly formatted JSON body where the email field contains not-an-email, or a date field holds an impossible value, is exactly 422 territory.

Notice that the definition is really a process of elimination — the RFC rules out two other codes by name:

  • Not 415, because the server does understand the content type (application/json was fine).
  • Not 400, because the syntax is correct (the JSON parsed).

What's left is meaning. The server got all the way to interpreting your data and found it unprocessable.

One piece of trivia that trips people up: 422 was born in the WebDAV spec (RFC 4918, 2007), not core HTTP — which is why older references call it "a WebDAV code" and warn you off using it in plain REST APIs. That advice is obsolete. RFC 9110 (2022) promoted 422 into the core HTTP semantics standard and renamed it 422 Unprocessable Content. It's now as "official" for general HTTP use as 400 or 404. You'll see both names in the wild — the status code is what matters; the reason phrase never did.

Syntax vs. Semantics: The Line Between 400 and 422

This is the distinction the whole code turns on, so here's the mental model:

  • 400 = "I can't even read this." The request is malformed at the protocol or format level — broken JSON, invalid encoding, a body that doesn't match its own Content-Type. Parsing failed.
  • 422 = "I read it fine, but it doesn't make sense." Parsing succeeded. The failure happened at the validation or business-rule level.

A quick test that resolves most cases: could a generic JSON parser with no knowledge of your business rules reject this request?

  • {"email": "test@ → the parser itself chokes → 400
  • {"email": "not-an-email"} → the parser is perfectly happy; only your validation knows it's wrong → 422
  • {"quantity": -3} → valid JSON, valid number, nonsensical order → 422
  • {"startDate": "2026-09-10", "endDate": "2026-09-01"} → each field is fine alone; together they violate a business rule → 422

Honesty requires saying: the boundary is blurry in practice, and many APIs use 400 for every client-side data problem. That's not a spec violation — 400 is defined broadly enough to cover "the server won't process this due to a client error." But using both codes buys you something concrete: clients can distinguish "my request is structurally broken — this is a bug in my serialization code" (400) from "the user typed something invalid — show them the field errors" (422). That's a genuinely different code path on the client, and the status code is the cheapest possible way to signal it.

There's one gray zone worth calling out: missing required fields. Some teams treat a missing field as structural (400), others as validation (422). Either is defensible — what matters is that you pick one and apply it consistently across your API. Most validation libraries report missing fields alongside format errors, so in practice they usually end up as 422 with everything else.

422 vs 400 vs 415 vs 409

Four codes cover "there's something wrong with your request data," and they answer different questions:

CodeMeaningUse when
400 Bad RequestMalformed requestThe body can't be parsed at all — invalid JSON, bad encoding, broken framing
415 Unsupported Media TypeWrong content typeThe client sent XML to a JSON-only endpoint, or an unexpected Content-Type
422 Unprocessable EntityValid syntax, invalid meaningThe body parsed, but fails validation or business rules
409 ConflictValid request, conflicting stateThe data is fine, but the current state of the resource rejects it — duplicate username, stale version, edit collision

The processing order mirrors the table: a server checks the content type first (415), then parses (400), then validates (422), then applies it against current state (409). Each code marks the stage where the request died.

The 422-vs-409 pair deserves a second look because both involve "valid request, rejected anyway." The difference is what rejected it. If the request would fail no matter what state the server was in — a negative price, an impossible date — that's 422. If the same request would have succeeded a moment earlier or later — the username was taken, the record was modified by someone else — that's 409. Validation is stateless; conflict is stateful.

When to Use 422 (and When Not To)

Reach for 422 when:

  1. The request is syntactically valid but fails semantic validation. The canonical case — well-formed body, invalid content.
  2. Form field validation fails. Invalid email format, password too weak, phone number in the wrong format. Return the full list of field errors in the body so the UI can annotate the form in one round trip.
  3. Business logic validation fails. A booking date in the past, a negative quantity, an end date before a start date, a discount code applied to an ineligible cart.
  4. Cross-field rules fail. Each field validates alone, but the combination is invalid.

And don't use 422 when:

  • The body won't parse. That's 400 — the request never got far enough for semantics to matter.
  • The content type is wrong. That's 415.
  • The data is valid but conflicts with existing state. That's 409.
  • The user isn't authenticated or lacks permission. That's 401 or 403 — auth failures are not validation failures.

You're also in good company: several major frameworks made 422 their default validation-failure response. Rails returns 422 when model validations fail (and renders form errors with it), Laravel returns 422 from its validator for JSON requests, and FastAPI automatically returns 422 whenever a request fails Pydantic validation. If you've consumed almost any modern API — GitHub's REST API included — you've already been handling 422s.

Returning 422 Correctly

The status code is only half the job. A 422 with an empty body tells the client that validation failed but not what to fix — and "what to fix" is the entire point. Always pair 422 with a structured error body. Here's the shape the site's /code/422 page uses as its canonical example:

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
 
{"errors": {"email": "Invalid email format"}}

Express / Node.js

Validate first, return early with the full set of field errors:

app.post("/api/users", (req, res) => {
  const errors: Record<string, string> = {};
 
  if (!isValidEmail(req.body.email)) {
    errors.email = "Invalid email format";
  }
  if ((req.body.password ?? "").length < 12) {
    errors.password = "Password must be at least 12 characters";
  }
  if (Object.keys(errors).length > 0) {
    return res.status(422).json({ errors });
  }
 
  const user = createUser(req.body);
  res.status(201).json(user);
});

Two details worth copying: collect all the errors before responding (nobody enjoys fixing one field per submission), and let Express's own body parser handle the 400 case — if the JSON is malformed, express.json() rejects it before your handler ever runs, which keeps the 400/422 boundary exactly where the spec puts it.

Next.js App Router

In a route handler, run your schema validation and return the issues with a 422:

// app/api/users/route.ts
import { NextResponse } from "next/server";
import { z } from "zod";
 
const UserSchema = z.object({
  email: z.email("Invalid email format"),
  password: z.string().min(12, "Password must be at least 12 characters"),
});
 
export async function POST(request: Request) {
  let body: unknown;
  try {
    body = await request.json();
  } catch {
    // Malformed JSON never reaches validation - that's a 400
    return NextResponse.json({ error: "invalid_json" }, { status: 400 });
  }
 
  const result = UserSchema.safeParse(body);
  if (!result.success) {
    return NextResponse.json(
      { errors: z.flattenError(result.error).fieldErrors },
      { status: 422 },
    );
  }
 
  const user = await createUser(result.data);
  return NextResponse.json(user, { status: 201 });
}

Note how the two failure modes get different codes: request.json() throwing means the body was unparseable (400), while safeParse failing means it parsed but didn't validate (422). The try/catch is the syntax/semantics boundary, expressed in code.

REST API JSON body

For a public API, consider the standard Problem Details format (RFC 9457) so clients get a predictable envelope across all your error responses:

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json
 
{
  "type": "https://api.example.com/errors/validation",
  "title": "Validation failed",
  "status": 422,
  "errors": [
    { "field": "email", "message": "Invalid email format" },
    { "field": "quantity", "message": "Must be greater than zero" }
  ]
}

Whatever shape you choose, keep it identical for every 422 your API emits. Clients will write one error-rendering function against it; inconsistent shapes mean they write one per endpoint, badly.

(No NGINX or Apache section this time — 422 is an application-level verdict about the meaning of a request body, which is exactly the thing a web server or proxy can't judge. If you're seeing 422s, they came from the app behind the proxy.)

422 and SEO

422 is overwhelmingly an API response to POST, PUT, and PATCH requests, so it rarely intersects with SEO at all — crawlers index pages they GET, and a GET should essentially never return 422.

If a real page URL does return 422, Google treats it like any other 4xx error (except 429): the page is not considered for indexing, and if it was previously indexed, it will eventually be dropped from the index. Per Google's crawling documentation, 4xx codes other than 429 don't affect crawl rate — the URL just quietly falls out of search. So if you find 422s in your server logs attached to indexable URLs, that's a bug to fix, not a signal to tune: those pages are invisible to search for as long as they return it.

Common Pitfalls

  1. Returning 200 with an error body. {"success": false, "errors": [...]} behind a 200 breaks every HTTP client's success/failure branching. Fetch wrappers, interceptors, retry logic, and monitoring all key off the status code — use it.
  2. Using 422 for malformed requests. If JSON.parse threw, the request never reached semantics. That's a 400. Returning 422 for unparseable bodies erases exactly the distinction the code exists to make.
  3. Returning one error at a time. Validating field-by-field and bailing on the first failure forces users through submit-fix-submit-fix loops. Validate everything, return the complete list.
  4. Using 422 for auth and permission failures. "Your session expired" is a 401; "you can't edit this" is a 403. Validation codes are for the request's content, not the requester.
  5. Using 422 where 409 belongs (and vice versa). "Username is already taken" depends on current server state — that's a 409. "Username contains invalid characters" would fail in any state — that's 422. Mixing them up hides whether retrying with the same data could ever work.
  6. Letting validation errors surface as 500s. An unhandled validation exception that bubbles up as 500 turns a client mistake into a server-error page, pollutes your error tracking, and tells the client to retry — the one thing guaranteed not to help. Catch validation failures explicitly and translate them to 422.
  7. Inconsistent error body shapes. One endpoint returns {"errors": {...}}, another {"message": "..."}, a third an array. Pick one schema (RFC 9457 if you want a standard) and enforce it everywhere.

Wrapping Up

422 is the status code that makes validation errors machine-readable. The rules of thumb:

  • 400 = can't parse it, 422 = parsed it, but it doesn't make sense — the generic-parser test resolves most cases
  • Check content type (415), then syntax (400), then meaning (422), then state (409) — return the code for the stage that failed
  • RFC 9110 renamed it Unprocessable Content and made it a core HTTP code — "it's WebDAV-only" hasn't been true since 2022
  • Always ship a structured error body with all the field errors, in one consistent shape
  • Validation is stateless (422); conflict is stateful (409); auth is neither (401/403)

For more, see our pages on 422 Unprocessable Entity, 400 Bad Request, and 409 Conflict. The other side of the boundary — what makes a request malformed enough to earn a 400 — gets the full treatment in our understanding 400 Bad Request post.

Full Reference

422 Unprocessable Entity

The request was well-formed but was unable to be followed due to semantic errors.

Related Status Codes

🤨400Bad Request🔐401Unauthorized💳402Payment Required🚫403Forbidden🔍404Not Found🙅405Method Not Allowed🍽️406Not Acceptable🎫407Proxy Authentication Required⏰408Request Timeout⚔️409Conflict👻410Gone📏411Length Required❌412Precondition Failed📦413Payload Too Large📜414URI Too Long📼415Unsupported Media Type📖416Range Not Satisfiable😞417Expectation Failed🫖418I'm a Teapot🚪421Misdirected Request🤔422Unprocessable Entity🔒423Locked🎯424Failed Dependency⏰425Too Early⬆️426Upgrade Required🔑428Precondition Required🚦429Too Many Requests📋431Request Header Fields Too Large⚖️451Unavailable For Legal Reasons
Back to Blog

Popular Status Codes

  • 200 OK
  • 301 Moved Permanently
  • 302 Found
  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable

Compare Codes

  • 401 vs 403
  • 301 vs 302
  • 404 vs 410
  • 500 vs 502
  • Compare any codes →

Categories

  • Informational
  • Success
  • Redirection
  • Client Error
  • Server Error
  • NGINX
  • Cloudflare
  • AWS ELB
  • Microsoft IIS

Tools

  • Cheat Sheet
  • Status Code Quiz
  • URL Checker
  • API Playground
  • Blog

© 2026 SiteError.com. All rights reserved.