tiangolo/fastapi · error · HTTPException

{"errors": exc.errors(), "body": body.decode()}

Error message

{"errors": exc.errors(), "body": body.decode()}

What it means

Legacy-style (default-parameter) variant of error 12: the same ValidationErrorLoggingRoute intercepts RequestValidationError on POST / and raises HTTPException(422) with detail {'errors': exc.errors(), 'body': <decoded raw body>}. Identical behavior; only the route signature style differs (numbers: list[int] = Body() vs Annotated).

Solutions

  1. Send a valid JSON array of ints with Content-Type: application/json.
  2. Pre-check the payload client-side.
  3. Use the Annotated variant for forward compatibility.

Example fix

// before
POST /  body: 5
// after
POST /  body: [1,2,3]
Defensive patterns

Strategy: validation

Validate before calling

import json, httpx
def is_int_list(body: str) -> bool:
    try:
        parsed = json.loads(body)
    except json.JSONDecodeError:
        return False
    return isinstance(parsed, list) and all(isinstance(x, int) for x in parsed)
payload = '[1,2,3]'
if is_int_list(payload):
    resp = httpx.post('http://localhost:8000/', content=payload, headers={'Content-Type': 'application/json'})

Type guard

def is_list_of_int(value: object) -> bool:
    return isinstance(value, list) and all(isinstance(x, int) and not isinstance(x, bool) for x in value)

Try / catch

try:
    resp = httpx.post(url, content=payload, headers={'Content-Type': 'application/json'})
    resp.raise_for_status()
except httpx.HTTPStatusError as e:
    if e.response.status_code == 422:
        detail = e.response.json()
        ...

Prevention

When it happens

Trigger: POST / with a body that fails to parse as list[int]. The custom handler returns the errors plus the raw submitted body.

Common situations: Malformed JSON, sending a scalar instead of a list, wrong content-type, or relying on the default FastAPI 422 shape while this route echoes the body.

Related errors


AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11). Data as JSON: /api/errors/6a8554dbe71908b3. Report an issue: GitHub.

Appendix: source

Thrown at docs_src/custom_request_and_route/tutorial002_py310.py:18

from collections.abc import Callable

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: list[int] = Body()):
    return sum(numbers)

View on GitHub (pinned to 3e8d1526d8)