422 Unprocessable Entity: {"detail":[{"type":"missing","loc":["body","email"],"msg":"Field required"}]}

Python · updated 2026-11-03

Returned by FastAPI itself, before your endpoint function runs, when request validation fails. Your code never sees the request, so nothing in your handler logs it unless you add a handler for RequestValidationError.

HTTP/1.1 422 Unprocessable Entity
content-type: application/json

{
  "detail": [
    {
      "type": "missing",
      "loc": ["body", "email"],
      "msg": "Field required",
      "input": {"name": "Ada"}
    }
  ]
}

Quick fix

Read loc in the response: the first item says where FastAPI looked (body, query, path, header), the rest is the field. Then make the client send that field in that place with the right type, or change the model if the field should be optional.

  1. Read detail[].loc and detail[].msg in the 422 response.
  2. If loc starts with "query" but you sent JSON, the parameter is declared as a plain argument: use a Pydantic model or Body() for JSON.
  3. If the field is optional, give it a default: email: str | None = None.
  4. Check the client sends Content-Type: application/json with a JSON body, not form data.
  5. Log validation failures with a RequestValidationError handler so they show up in your error tracker.
python
from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from pydantic import BaseModel

app = FastAPI()

class Signup(BaseModel):
    name: str
    email: str | None = None   # optional fields need a default

@app.post("/signup")
async def signup(body: Signup):  # a model = read from the JSON body
    return {"ok": True}

@app.exception_handler(RequestValidationError)
async def log_422(request: Request, exc: RequestValidationError):
    log.warning("422 on %s: %s", request.url.path, exc.errors())
    return JSONResponse(status_code=422, content={"detail": exc.errors()})

Why it happens

A JSON field read as a query parameter

A plain function argument like def create(email: str) is a query parameter. Sending it in a JSON body leaves the query empty, so FastAPI reports loc ["query", "email"] as missing.

Optional fields without a default

In Pydantic v2, a field typed str | None is still required unless it has a default. Clients that omit it get "Field required".

Form data or the wrong content type

An HTML form or a client sending application/x-www-form-urlencoded to an endpoint that expects a JSON model fails validation on every field.

Type mismatches

A string where an int is declared, a date in the wrong format, or an enum value that isn't in the list returns a 422 with the expected type in msg.

Other fixes

  • Open /docs on your app to see exactly where each parameter is expected (query, body, path).
  • For form posts, declare Form() parameters and install python-multipart.
  • Validation errors are client mistakes, so they are not exceptions in your code: log them deliberately if you want to see them.

Same error, other wording

  • {"detail":[{"type":"int_parsing","loc":["query","page"],"msg":"Input should be a valid integer, unable to parse string as an integer"}]}
  • {"detail":[{"type":"json_invalid","loc":["body",0],"msg":"JSON decode error"}]}
  • {"detail":[{"type":"missing","loc":["query","item_id"],"msg":"Field required"}]}

Let your coding agent catch the next one

Runheed records this error in production with the stack trace, the breadcrumbs before it and the agent session that shipped the code, and serves it to Claude Code, Cursor or any MCP client. With it connected, you can ask:

> Find the 422 responses from our FastAPI app, group them by endpoint and the field in loc, and fix either the client calls or the models so the fields match.

Plans from $29/moSee what your agent readsSee the setupCancel any time from the billing portal

Related errors

References: FastAPI docs: handling errors (RequestValidationError), FastAPI docs: request body