'How to change the default Pydantic error message using FastAPI?

Is there any way to change the default response from Pydantic so that "msg" is "message"?

{
    "detail": [
        {
            "loc": [
                "body",
                "password"
            ],
            "msg": "Password should at least 8 characters long.",
            "type": "value_error"
        }
    ]
}


Solution 1:[1]

That looks like a JSON response and Pydantic by itself does not give out a JSON response for ValidationError's. It should just be a regular raise-d Exception like this:

In [2]: from pydantic import BaseModel, constr

In [3]: class Credentials(BaseModel):
   ...:     password: constr(min_length=8)
   ...: 

In [4]: Credentials(password="xyz")
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
Input In [4], in <cell line: 1>()
----> 1 Credentials(password="xyz")

...

ValidationError: 1 validation error for Credentials
password
  ensure this value has at least 8 characters (type=value_error.any_str.min_length; limit_value=8)

I think you are using FastAPI (which has Pydantic integration) and this JSON response is actually FastAPI's built-in error response when the request has ValidationError's, as described in the FastAPI docs on Handling Errors. I can replicate a similar error format with this example:

from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel, constr

class Credentials(BaseModel):
    password: constr(min_length=8)

app = FastAPI()

@app.post("/login")
async def login(credentials: Credentials):
    print(credentials)  # This is just as an example!
    return JSONResponse(status_code=200)
$ curl -s --header "Content-Type: application/json" --request POST --data '{"password":"xyz"}' http://localhost:8000/login | jq
{
  "detail": [
    {
      "loc": [
        "body",
        "password"
      ],
      "msg": "ensure this value has at least 8 characters",
      "type": "value_error.any_str.min_length",
      "ctx": {
        "limit_value": 8
      }
    }
  ]
}

To change the response body, check the FastAPI docs on Use the RequestValidationError body that shows you can access the default detail list of errors, which you can then edit or copy over to a customized details list, and then set it as the content of the JSON error response.

from fastapi import FastAPI, Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from pydantic import BaseModel, constr

class Credentials(BaseModel):
    password: constr(min_length=8)

app = FastAPI()

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    # Get the original 'detail' list of errors
    details = exc.errors()
    modified_details = []
    # Replace 'msg' with 'message' for each error
    for error in details:
        modified_details.append(
            {
                "loc": error["loc"],
                "message": error["msg"],
                "type": error["type"],
            }
        )
    return JSONResponse(
        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
        content=jsonable_encoder({"detail": modified_details}),
    )


@app.post("/login")
async def login(credentials: Credentials):
    print(credentials)  # Just as an example!
    return JSONResponse(status_code=200)
$ curl -s --header "Content-Type: application/json" --request POST --data '{"password":"xyz"}' http://localhost:8000/login | jq
{
  "detail": [
    {
      "loc": [
        "body",
        "password"
      ],
      "message": "ensure this value has at least 8 characters",
      "type": "value_error.any_str.min_length"
    }
  ]
}

Of course, you can also simply just define your own custom content and JSONResponse.

Solution 2:[2]

Building upon the previous answer here, you can use a validation exception handler, as shown below:

from fastapi import Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    for error in exc.errors(): 
        error['message'] = error.pop('msg')
    
    return JSONResponse(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, content=jsonable_encoder({"detail": exc.errors()}))

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Chris