Essay

Validation errors are part of your API

Teams treat a 422 Unprocessable Entity like an embarrassment — something to soften into a vague 400 with "Invalid request". That hides the whole point of a schema. When Pydantic rejects a payload, it already knows which field failed, why, and what was expected. That detail is an API feature, not a leak.

The happy path is only half the contract

OpenAPI documents the request body you hope to receive. Clients also need to know how you reject the rest. Structured errors let a UI highlight email, show that age must be an integer, and retry without a support ticket that says “it didn’t work.”

FastAPI’s default validation response is already useful: a list of errors with loc, msg, and type. Before you replace it, ask whether you are improving clarity for clients — or just making the server feel quieter.

What a useful error looks like

{
  "detail": [
    {
      "type": "int_parsing",
      "loc": ["body", "age"],
      "msg": "Input should be a valid integer, unable to parse string as an integer",
      "input": "thirty"
    }
  ]
}

A human can map that to a form field. A machine can branch on type and loc. A single string "Bad request" forces every client to guess.

Don’t conflate validation with domain rules

Schema failures belong at the edge: wrong types, missing required fields, malformed emails. Domain failures — “email already registered,” “insufficient balance” — are different. They usually deserve their own status codes and payloads, not a stretched ValidationError.

Mixing them produces mush: either every business rule becomes a custom validator nobody can reuse, or every schema miss becomes a generic application exception with no field path. Keep parse errors and business errors on separate tracks.

Customize when you have a reason

Stable error envelopes, redaction of sensitive input values, or translating messages for product copy are good reasons to wrap the default handler. Cosmetic silence is not. If you strip loc because “clients shouldn’t see internals,” remember those locations are the public field names you already published in the schema.

Log the structured error server-side. Return enough for the caller to fix the payload. That is the same discipline as runtime validation itself: fail clearly, at the boundary, with a shape both sides can rely on.

Bad data will arrive. Your API’s job is to say exactly how it failed — not that it felt awkward about saying so.