400 error code in rest api: The Nine Reasons and the Three That Hide Behind "Bad Request"

Sean

Platform Writer

Jun 18, 2026
6 min read

The fastest answer to “what does a 400 mean” is “the request was bad.” That is what the RFC says. The honest version covers the nine specific reasons a 400 fires in a real REST API, which three are most often mislabeled (and should be 422 instead), and how to return a 400 with a useful error body so the client can actually fix their request. A bare 400 with no body is a debugging time-bomb.

400 error code in rest api: The Nine Reasons and the Three That Hide Behind 'Bad Request'

Table of contents

The short version: 400 means “your request is malformed at the HTTP or syntax level.” Use 400 for malformed JSON, missing required headers, wrong content type, and oversized payloads. Use 422 (Unprocessable Entity) for “the syntax is fine but the values are invalid” — missing required fields, validation errors, business-rule failures. Most teams conflate the two; that conflation is the source of much of the confusion.

400 error code in rest api: the nine reasons and the three that hide behind bad request

Table of contents

The direct answer

The nine reasons a REST API returns 400, in rough order of frequency:

  1. Malformed JSON. The request body is not valid JSON.
  2. Missing required header. The request is missing a header the server needs (Authorization, Content-Type, X-API-Key).
  3. Wrong content type. The Content-Type header does not match the body (application/json sent with form-encoded data, etc.).
  4. Missing required query parameter. A query parameter the server requires is missing.
  5. Invalid query parameter value. A query parameter is present but its value is not in the allowed set.
  6. Oversized payload. The request body exceeds the server’s configured maximum.
  7. Invalid URL path. The URL path does not match any route (often a 404, but sometimes a 400 if the format is wrong).
  8. Invalid HTTP method. The route exists but the method is not allowed (should be 405).
  9. Invalid character encoding. The request body has bytes that are not valid UTF-8.

For most of these, the fix on the client side is to look at the response body, which should describe what was wrong. If the body is empty or generic, the server is misconfigured.

The nine reasons a 400 fires

1. Malformed JSON

The most common cause. The client sent { "name": "acme" } but forgot a closing brace, or sent {"name": acme} (unquoted string), or sent {"name": "acme",} (trailing comma).

The server cannot parse the JSON. The framework’s JSON parser throws. The server returns 400.

The fix on the client: validate JSON before sending. Most languages have a JSON validator (JSON.parse with try/catch in JavaScript, json.loads with try/except in Python). Lint the payload in CI.

The fix on the server: return 400 with a clear message. {"error": "invalid JSON at line 3: expected '}'"} is useful. {"error": "Bad Request"} is not.

2. Missing required header

The server requires Authorization: Bearer <token> for every request. The client forgot to include it. The server returns 400 (or 401, depending on the framework).

The fix: return 401 when the header is missing or invalid, not 400. 401 means “you need to authenticate.” 400 means “your request is malformed.” The distinction matters because clients handle 401 differently (refresh token, redirect to login) than 400.

Some frameworks return 400 for missing headers by default. Configure them to return 401.

3. Wrong content type

The server expects Content-Type: application/json but the client sent Content-Type: application/x-www-form-urlencoded. The server cannot parse the body. Returns 400.

The fix on the client: always set Content-Type correctly. The two most common values: application/json for JSON bodies, application/x-www-form-urlencoded for form posts. multipart/form-data for file uploads.

The fix on the server: be explicit about what content types you accept. Reject with 415 (Unsupported Media Type) for content types you do not handle, not 400.

4. Missing required query parameter

The endpoint requires ?user_id=<id>. The client forgot the parameter. The server returns 400.

The fix: validate query parameters in middleware. Reject with 400 and a list of the missing parameters.

5. Invalid query parameter value

The endpoint accepts ?status=active|inactive|pending. The client sent ?status=foo. The server returns 400.

The fix: validate the value against an allowed set. Return 400 with the allowed values in the error body.

6. Oversized payload

The server has a max body size of 1 MB. The client sent a 5 MB JSON. The server returns 413 (Payload Too Large) — but some servers return 400.

The fix on the server: return 413, not 400. 413 is the specific code for “the payload is too big.”

The fix on the client: check the size before sending. For large uploads, use a chunked upload endpoint.

7. Invalid URL path

The client sent POST /v1/orders but the route is POST /v1/order (singular). The server returns 404 (Not Found) or 405 (Method Not Allowed). Some servers return 400.

The fix: return 404 for unknown routes, 405 for known routes with the wrong method. Both are more specific than 400.

8. Invalid HTTP method

The client sent GET /v1/orders but the route only accepts POST. The server returns 405 (Method Not Allowed).

The fix: return 405, not 400. 405 is the specific code for “the route exists but the method is wrong.”

9. Invalid character encoding

The request body has bytes that are not valid UTF-8. Most frameworks return 400.

The fix: send UTF-8. Always. Most languages default to UTF-8 for JSON serialization. Watch out for legacy encodings in old clients.

The three reasons 400 is the wrong code (use 422 instead)

422 (Unprocessable Entity) is for “the syntax is fine but the values are invalid.” The RFC 4918 definition: “the server understands the content type of the request entity, and the syntax of the request entity is correct, but it was unable to process the contained instructions.”

The three cases where 422 is the right code:

1. Missing required field. The client sent {"name": "Acme"} to a POST /users endpoint that requires email. The JSON is valid, the syntax is correct, but the value is incomplete. Return 422.

2. Invalid field value. The client sent {"email": "not-an-email"}. The JSON is valid, but the value does not match the expected format. Return 422.

3. Business rule violation. The client sent {"quantity": -1} to a POST /orders endpoint that requires positive quantities. The JSON is valid, but the business rule says no. Return 422.

The distinction matters because 422 lets the client distinguish “I sent malformed JSON” (400) from “my values were wrong” (422). Some client libraries retry on 400 (because they think the request was malformed by a transient network issue) but not on 422 (because retrying with the same payload will fail again).

How to return a 400 with a useful error body

The minimum useful error body:

{
  "error": "invalid_json",
  "message": "Request body is not valid JSON. Expected ',' or '}' at line 3.",
  "details": {
    "line": 3,
    "column": 17,
    "hint": "Check for trailing commas or unquoted strings."
  }
}

The fields:

  • error — a machine-readable code (invalid_json, missing_header, invalid_value).
  • message — a human-readable description.
  • details — optional structured info for debugging (line/column for JSON errors, list of missing fields for validation errors, etc.).

The HTTP status code in the response status line matches the error field’s semantic class:

  • 400 for malformed requests.
  • 401 for missing/invalid auth.
  • 403 for forbidden (auth valid but lacks permission).
  • 404 for not found.
  • 405 for method not allowed.
  • 409 for conflict (e.g. duplicate key).
  • 413 for payload too large.
  • 415 for unsupported media type.
  • 422 for validation errors.
  • 429 for rate limited.
  • 500 for server errors.

Most teams use 400 and 500 as the only two codes, which is why the 400 is so ambiguous. The right answer is to use the specific codes.

The five most common client mistakes

  1. Not reading the response body. The server returned 400 with {"error": "invalid_json", "message": "..."} and the client only checked the status code. Read the body.

  2. Not validating JSON before sending. Use a JSON validator in the client. Most languages have one. Lint payloads in CI.

  3. Hardcoding URLs. https://api.example.com/v1/orders works until the API renames the route. Use a client SDK that handles versioning.

  4. Not handling redirects. The server returns 301 or 308 with a new URL. The client follows the redirect and sends the same body to the new URL — but the new URL expects a different method. Handle redirects explicitly.

  5. Assuming retry is safe. Some 400s are safe to retry (network timeout during send). Others are not (the server already processed the request and rejected it). Do not retry on 400 by default.

The diagnostic flow that catches most 400s

For clients hitting an unexpected 400:

  1. Check the response body. The server should have explained what was wrong. If the body is empty or generic, the server is misconfigured.
  2. Check Content-Type. Match the server’s expected content type.
  3. Validate the JSON. Use a JSON validator. Lint the payload.
  4. Check headers. Authorization, X-API-Key, Accept — whatever the server requires.
  5. Check query parameters. Required params, allowed values.
  6. Check the URL. Path version, route format.
  7. Try with curl. A bare curl -v request shows the exact bytes the server is receiving. Compare to what your client is sending.

Most 400s are caught at steps 1-3.

If you are designing an API for a backend service on a platform like RunxBuild’s backend services, the framework (FastAPI, Express, Django REST) handles status code generation; the right code is a one-line change in the handler. For what running that API at production scale costs, the RunxBuild hosting calculator gives you the per-month number.

FAQ

What is the difference between 400 and 422?

400 is for malformed requests (invalid JSON, missing headers). 422 is for valid syntax but invalid values (missing required field, validation error). The distinction lets clients handle the two cases differently.

What is the difference between 400 and 401?

400 is for malformed requests. 401 is for missing or invalid authentication. The client should respond to 401 by re-authenticating; it should respond to 400 by fixing the request.

What is the difference between 400 and 403?

400 is for malformed requests. 403 is for “you authenticated successfully but lack permission.” Use 403 for authorization failures; 401 for authentication failures.

What is the difference between 400 and 404?

400 is for malformed requests. 404 is for “the resource does not exist.” Use 404 for unknown routes or missing resources; 400 for malformed payloads.

What is the difference between 400 and 409?

400 is for malformed requests. 409 (Conflict) is for “the request is well-formed but conflicts with the current state” (duplicate key, version mismatch, etc.).

Can a 400 be retried?

Some 400s are transient (network issue corrupted the JSON). Most are not (the client sent invalid values). Do not retry 400 by default; check the response body first.

Should I include stack traces in 400 responses?

No. Stack traces leak implementation details. Return a stable error code and a short message. The server should log the full trace for debugging.

Why does my client library retry on 400?

Some libraries assume any 4xx is a permanent failure and retry. Others assume 4xx is safe to retry (because the request was malformed and a fresh retry might succeed if the bug is fixed). Check your library’s retry policy. Most should not retry on 400 by default.

FAQ

What is the difference between 400 and 422?

400 is for malformed requests (invalid JSON, missing headers). 422 is for valid syntax but invalid values (missing required field, validation error).

What is the difference between 400 and 401?

400 is for malformed requests. 401 is for missing or invalid authentication.

What is the difference between 400 and 403?

400 is for malformed requests. 403 is for “you authenticated successfully but lack permission.”

What is the difference between 400 and 404?

400 is for malformed requests. 404 is for “the resource does not exist.”

What is the difference between 400 and 409?

400 is for malformed requests. 409 (Conflict) is for “the request is well-formed but conflicts with the current state.”

Can a 400 be retried?

Some 400s are transient. Most are not. Do not retry 400 by default; check the response body first.

Should I include stack traces in 400 responses?

No. Stack traces leak implementation details. Return a stable error code and a short message.

Why does my client library retry on 400?

Different libraries have different retry policies. Most should not retry on 400 by default. Check your library’s behavior.