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

  1. Send a JSON array of integers with Content-Type: application/json, e.g. [1,2,3].
  2. Inspect the returned detail.errors[] to find the failing field and type.
  3. 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

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


AI-assisted analysis of tiangolo/fastapi@42a41db11f (2026-08-04). Data as JSON: /data/errors/621b2a18720e8250.json. Report an issue: GitHub.