HTTP status code · Client errors (4xx)
422 Unprocessable Content
422 Unprocessable Content means the server understood the format of the request body and parsed it, but the data inside cannot be processed. The most common cause is a validation failure: a required field is missing, a value has the wrong type, or two fields contradict each other.
| Class | 4xx, Client errors |
|---|---|
| Also known as | Unprocessable Entity |
| Defined in | RFC 9110 §15.5.21 |
| Cacheable by default | Only with explicit Cache-Control or Expires |
| Safe to retry | Not until the request body is corrected; the same payload fails the same validation |
| Relevant headers |
|
What 422 means
RFC 9110, section 15.5.21, pins 422 between two other codes: the content type is supported (otherwise 415) and the syntax is correct (otherwise 400), yet the instructions in it cannot be carried out. The code started in WebDAV (RFC 4918) as "Unprocessable Entity" and was folded into core HTTP in 2022 under the name "Unprocessable Content". Both names refer to the same number.
In practice 422 has become the standard answer for form and API validation errors. FastAPI returns it automatically when a request does not match the Pydantic model, with a detail array naming each failing field. Laravel returns it when a FormRequest fails validation, and Rails scaffolds render failed saves with status :unprocessable_entity.
The response body is where the value is. A 422 with no detail forces the client to guess which field was wrong; a list of field paths and messages lets a form highlight the right input.
Common causes
If you are visiting the site
- A form was submitted with a field the site rejects: an invalid email, a date in the past, a password below the minimum length.
- A browser extension or autofill put an unexpected value into a hidden field.
If you run the server
- The client sends a field under a different name or type than the schema expects, such as "userId" instead of "user_id", or the number 42 as the string "42" to a strict validator.
- In FastAPI, a parameter declared without a default becomes required, so a missing query parameter or body field yields 422 with "field required".
- A body sent as form data to an endpoint that expects JSON (or the reverse) in FastAPI shows up as 422, because every field appears to be missing.
- Business rules on otherwise valid data: end date before start date, quantity above stock, a coupon that expired.
How to fix it
If you are visiting the site
- Read the message next to each field; the form usually says which value it refuses.
- Disable autofill or extensions for that page and type the values by hand.
If you run the server
- Log or print the response body: FastAPI puts each problem in detail[].loc and detail[].msg, which point straight at the field.
- Check the Content-Type the client sends matches what the endpoint parses (application/json vs multipart/form-data).
- Compare the payload against the schema or OpenAPI document; watch for camelCase vs snake_case and for numbers sent as strings.
- Return 422 only for semantic problems. If the JSON cannot even be parsed, answer 400 so clients can tell a broken body from invalid values.
How to send 422
app.post('/bookings', (req, res) => {
res.status(422).json({ error: 'Validation failed', fields: { endDate: 'Must be after startDate' } });
});// app/bookings/route.ts
export async function POST() {
return Response.json(
{ error: 'Validation failed', fields: { endDate: 'Must be after startDate' } },
{ status: 422 }
);
}mux.HandleFunc("POST /bookings", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnprocessableEntity) // 422
w.Write([]byte(`{"error":"Validation failed","fields":{"endDate":"Must be after startDate"}}`))
})from datetime import date
from fastapi import FastAPI
from pydantic import BaseModel, model_validator
app = FastAPI()
class Booking(BaseModel):
start_date: date
end_date: date
@model_validator(mode="after")
def check_dates(self):
if self.end_date <= self.start_date:
raise ValueError("end_date must be after start_date")
return self
# FastAPI answers 422 on its own when the body fails validation
@app.post("/bookings")
def create_booking(booking: Booking):
return bookingCommonly confused with
- 422 vs 400
- 400 is for a request the server cannot parse at all, such as broken JSON; 422 is for a body that parses fine but holds invalid values.
- 422 vs 409
- 409 means the data is valid but clashes with the current state, like a username already taken; 422 means the data itself is invalid.
- 422 vs 415
- 415 rejects the format (for example XML sent to a JSON-only API); 422 accepts the format and rejects the content.
Frequently asked questions
- Is it 422 Unprocessable Entity or Unprocessable Content?
- Both. RFC 4918 (WebDAV) called it Unprocessable Entity; RFC 9110 moved it into core HTTP in 2022 and renamed it Unprocessable Content. Frameworks and libraries still use either name, and the number did not change.
- Should I use 400 or 422 for validation errors?
- Use 400 when the request cannot be parsed (malformed JSON, wrong encoding) and 422 when it parses but the values fail your rules. Many public APIs use 400 for both; what matters most is being consistent and returning field-level details.
- Why does FastAPI return 422 when I send a request?
- FastAPI validates every request against the declared parameters and Pydantic models, and any mismatch returns 422 automatically. The usual culprits are a missing required field, a wrong type, or sending form data to an endpoint that expects a JSON body.
- How do I fix a 422 error in a form?
- Look at the error message next to each field and correct the value it names. If the page shows no details, open the browser developer tools, find the failed request in the Network tab and read its response body, which usually lists the invalid fields.
Last reviewed by Arielton Oberek.