400 Bad Request means the server couldn't understand the request: the syntax is broken, so it can't even be parsed. 422 Unprocessable Content means the request was well formed and understood, but the data fails validation. Malformed JSON is a 400. Valid JSON with an invalid email address is a 422.
400 Bad Request | 422 Unprocessable Content | |
|---|---|---|
| Meaning | I can't parse this | I parsed it, and it's invalid |
| Examples | Malformed JSON, wrong Content-Type, oversized or broken header, missing required query parameter | Invalid email, unknown enum value, end date before start date, quantity below zero |
| Defined in | RFC 9110 | RFC 9110 (originally WebDAV, RFC 4918, as "Unprocessable Entity") |
| Common in | Every server and proxy | Rails, Laravel, FastAPI, the GitHub API |
HTTP/1.1 400 Bad Request
Content-Type: application/json
{"error": "invalid_json", "message": "Unexpected token } in JSON at position 41"}
HTTP/1.1 422 Unprocessable Content
Content-Type: application/json
{"error": "validation_failed", "fields": {"email": "is not a valid email address"}}
Common causes of 400
- Malformed body. A trailing comma, single quotes, or a body that was truncated.
- Content type mismatch. Sending JSON without
Content-Type: application/json, or form data to an endpoint expecting JSON. - Bad headers or cookies. nginx returns
400 Bad Request: Request Header Or Cookie Too Largewhen headers exceedlarge_client_header_buffers. Clearing the site's cookies fixes it for visitors. - Invalid URL. Unencoded characters in the path or query string.
- Plain HTTP to an HTTPS port. nginx answers
400 The plain HTTP request was sent to HTTPS port.
Common causes of 422
- Field validation. A required field is empty, a string is too long, a format doesn't match.
- Business rules. A coupon that has expired, a duplicate username (some APIs use 409 for this), an invalid state transition.
- Framework defaults. FastAPI returns 422 for any request that fails its Pydantic model. Rails scaffold controllers return 422 when a record fails to save.
How to fix them
As a visitor: for a persistent 400 on one site, clear that site's cookies. Otherwise check the URL for typos.
As a developer calling an API: read the response body. Good APIs say exactly which field failed. Then:
- Log the exact request you sent, not the object you meant to send.
curl -vor your HTTP client's debug mode. - Validate the JSON (
jq . payload.json). - Check
Content-Typeand encoding. - Compare against the API's schema, including types:
"5"and5are different.
400 and 422 on a webhook endpoint
A webhook handler returning 400 or 422 almost always means the payload isn't the shape the code expected. A provider added a field, sent a different event type, used form encoding, or set a field to null. Retrying won't help, because the same payload will fail the same way.
Debug it by looking at the raw request rather than guessing. Point the provider (or a copy of its traffic) at a free Webhook Bin URL and you'll see the exact method, headers and body. If the problem is that your destination expects a different format, such as forwarding one provider's event into another service's API, a Webhook Relay transform function can rename fields, restructure the JSON or drop events before delivery.
Two rules for webhook handlers:
- Accept unknown fields and event types. Ignore what you don't need instead of failing validation on it, and acknowledge unhandled event types with
200. - Don't validate before you verify. Check the signature on the raw body first, then parse and validate.
When to return 400 vs 422 from your own API
Pick a convention and keep to it. A clear one: 400 for anything your parser or router rejects, 422 for well-formed requests that fail field or business validation. Always return a machine-readable body naming the failing fields. Never return 500 for bad input; that's a bug in your validation.
