{"id":"621b2a18720e8250","repo":"tiangolo/fastapi","slug":"errors-exc-errors-body-body-decode","errorCode":null,"errorMessage":"{\"errors\": exc.errors(), \"body\": body.decode()}","messagePattern":"\\{\"errors\": exc\\.errors\\(\\), \"body\": body\\.decode\\(\\)\\}","errorType":"http","errorClass":"HTTPException","httpStatus":422,"severity":"error","filePath":"docs_src/custom_request_and_route/tutorial002_an_py310.py","lineNumber":19,"sourceCode":"from collections.abc import Callable\nfrom typing import Annotated\n\nfrom fastapi import Body, FastAPI, HTTPException, Request, Response\nfrom fastapi.exceptions import RequestValidationError\nfrom fastapi.routing import APIRoute\n\n\nclass ValidationErrorLoggingRoute(APIRoute):\n    def get_route_handler(self) -> Callable:\n        original_route_handler = super().get_route_handler()\n\n        async def custom_route_handler(request: Request) -> Response:\n            try:\n                return await original_route_handler(request)\n            except RequestValidationError as exc:\n                body = await request.body()\n                detail = {\"errors\": exc.errors(), \"body\": body.decode()}\n                raise HTTPException(status_code=422, detail=detail)\n\n        return custom_route_handler\n\n\napp = FastAPI()\napp.router.route_class = ValidationErrorLoggingRoute\n\n\n@app.post(\"/\")\nasync def sum_numbers(numbers: Annotated[list[int], Body()]):\n    return sum(numbers)\n","sourceCodeStart":1,"sourceCodeEnd":31,"githubUrl":"https://github.com/tiangolo/fastapi/blob/42a41db11f6882807ac3c057b942178d53b97438/docs_src/custom_request_and_route/tutorial002_an_py310.py#L1-L31","documentation":"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.","triggerScenarios":"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].","commonSituations":"Clients sending wrong Content-Type, malformed JSON, or values that don't match the declared schema; debugging why a request was rejected.","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."],"exampleFix":"# before\nclient.post(\"/\", json={\"numbers\": [1,2,3]})\n# after\nclient.post(\"/\", json=[1,2,3])","handlingStrategy":"try-catch","validationCode":"# Validate payload matches list[int] before sending\nimport json\npayload = [1, 2, 3]\nassert isinstance(payload, list) and all(isinstance(i, int) for i in payload)","typeGuard":"def is_int_list(body) -> bool:\n    return isinstance(body, list) and all(isinstance(i, int) for i in body)","tryCatchPattern":"resp = client.post(\"/\", json=payload)\nif resp.status_code == 422:\n    detail = resp.json()[\"detail\"]\n    # detail has {\"errors\": [...], \"body\": \"...\"}\n    for err in detail[\"errors\"]:\n        print(err[\"loc\"], err[\"type\"])","preventionTips":["Send Content-Type: application/json.","Match the body to the declared schema (list[int]).","Use detail.errors[] to pinpoint the failing field."],"tags":["fastapi","validation","pydantic","custom-route"],"analyzedSha":"42a41db11f6882807ac3c057b942178d53b97438","analyzedAt":"2026-08-04T19:23:32.007Z","schemaVersion":2}