tiangolo/fastapi · error · HTTPException
{"errors": exc.errors(), "body": body.decode()}
Error message
{"errors": exc.errors(), "body": body.decode()} What it means
This is not a raised exception message but the detail payload of an HTTPException(422) constructed inside a custom APIRoute handler. When the original handler raises RequestValidationError (Pydantic body validation failure), the custom handler captures exc.errors() and the raw request body, then re-raises HTTP 422 with that combined detail. It exists to log/return exactly what failed validation and what was sent.
Source
Thrown at docs_src/custom_request_and_route/tutorial002_an_py310.py:19
from collections.abc import Callable
from typing import Annotated
from fastapi import Body, FastAPI, HTTPException, Request, Response
from fastapi.exceptions import RequestValidationError
from fastapi.routing import APIRoute
class ValidationErrorLoggingRoute(APIRoute):
def get_route_handler(self) -> Callable:
original_route_handler = super().get_route_handler()
async def custom_route_handler(request: Request) -> Response:
try:
return await original_route_handler(request)
except RequestValidationError as exc:
body = await request.body()
detail = {"errors": exc.errors(), "body": body.decode()}
raise HTTPException(status_code=422, detail=detail)
return custom_route_handler
app = FastAPI()
app.router.route_class = ValidationErrorLoggingRoute
@app.post("/")
async def sum_numbers(numbers: Annotated[list[int], Body()]):
return sum(numbers)
View on GitHub (pinned to 42a41db11f)
Solutions
- Send a JSON array of integers with Content-Type: application/json, e.g. [1,2,3].
- Inspect the returned detail.errors[] to find the failing field and type.
- Validate the payload against the schema before sending.
Example fix
# before
client.post("/", json={"numbers": [1,2,3]})
# after
client.post("/", json=[1,2,3]) Defensive patterns
Strategy: try-catch
Validate before calling
# Validate payload matches list[int] before sending import json payload = [1, 2, 3] assert isinstance(payload, list) and all(isinstance(i, int) for i in payload)
Type guard
def is_int_list(body) -> bool:
return isinstance(body, list) and all(isinstance(i, int) for i in body) Try / catch
resp = client.post("/", json=payload)
if resp.status_code == 422:
detail = resp.json()["detail"]
# detail has {"errors": [...], "body": "..."}
for err in detail["errors"]:
print(err["loc"], err["type"]) Prevention
- Send Content-Type: application/json.
- Match the body to the declared schema (list[int]).
- Use detail.errors[] to pinpoint the failing field.
When it happens
Trigger: POST / with a body that fails Pydantic validation for list[int], e.g. sending a JSON object, a string, or a list containing non-integers like [1,"a",3].
Common situations: Clients sending wrong Content-Type, malformed JSON, or values that don't match the declared schema; debugging why a request was rejected.
Related errors
- {"errors": exc.errors(), "body": body.decode()}
- e.errors(include_url=False)
- Invalid ID format, it must start with "isbn-" or "imdb-"
- Expected UploadFile, received: {type(__input_value)}
- Invalid YAML
AI-assisted analysis of tiangolo/fastapi@42a41db11f (2026-08-04).
Data as JSON: /data/errors/621b2a18720e8250.json.
Report an issue: GitHub.