Skip to content

Enhancement: Improve "key" property of validation error messages #4215

Open
@250MHz

Description

@250MHz

Summary

  • When performing validation on a request body that is a list, I'd like the validation error message's "extra" property to show the array index for the "key" property rather than showing "data". I'd like something similar for query parameters that are lists, where the array index is shown instead of the query parameter's name from the route handler's function signature.
  • When the name for a query parameter, cookie, or header differs from the name used in the route handler's signature, the error message's "key" property should show the alternative name instead of the one in the function.
  • Parameters in route handlers that are not the request body, but whose names start with "data", should not have their error message source set to "body".
  • When an ExtendedMsgSpecValidationError is raised, instead of turning the elements of loc to strings and joining with ".", the element should be wrapped in [] if it's an integer and otherwise prefixed with "." so that the error message "key" property better resembles the format that msgspec uses.

Basic Example

from typing import Annotated
from litestar import Litestar, post
from litestar.params import Parameter

@post("/")
async def foo(
    data: list[int],
    data_list: Annotated[list[int], Parameter(query="dataList")],
    foo_header: Annotated[int, Parameter(header="X-HEADER")],
    foo_cookie: Annotated[int, Parameter(cookie="my-cookie")],
) -> None: ...

app = Litestar(route_handlers=[foo])

Currently, curl -X POST 'http://localhost:8000?dataList=1&dataList=2&dataList=oops' -H "X-HEADER: oops" -b "my-cookie=oops" -d '[1, 2, "oops"]' will show:

{
    "status_code": 400,
    "detail": "Validation failed for POST /?dataList=1&dataList=2&dataList=oops",
    "extra": [
        {
            "message": "Expected `int`, got `str`",
            "key": "data",
            "source": "body"
        },
        {
            "message": "Expected `int`, got `str`",
            "key": "data_list",
            "source": "body"
        },
        {
            "message": "Expected `int`, got `str`",
            "key": "foo_header",
            "source": "header"
        },
        {
            "message": "Expected `int`, got `str`",
            "key": "foo_cookie",
            "source": "cookie"
        }
    ]
}

With the enhancement, the response will be:

{
    "status_code": 400,
    "detail": "Validation failed for POST /?dataList=1&dataList=2&dataList=oops",
    "extra": [
        {
            "message": "Expected `int`, got `str`",
            "key": "[2]",
            "source": "body"
        },
        {
            "message": "Expected `int`, got `str`",
            "key": "dataList[2]",
            "source": "query"
        },
        {
            "message": "Expected `int`, got `str`",
            "key": "X-HEADER",
            "source": "header"
        },
        {
            "message": "Expected `int`, got `str`",
            "key": "my-cookie",
            "source": "cookie"
        }
    ]
}

Drawbacks and Impact

  • Users who previously had a list query parameter might prefer the previous behavior when a parameter with only one element is passed.
    • In the above example, just one query parameter like ?dataList=abc will show "dataList[0]" as the key instead of just "dataList"
  • The following tests need to be modified to accommodate this change.
    • In tests/unit/test_signature/test_validation.py:
      test_invalid_input_attrs
      test_invalid_input_dataclass
      test_invalid_input_typed_dict
    • In tests/unit/test_plugins/test_pydantic/test_integration.py:
      test_signature_model_invalid_input

Unresolved questions

Should "data" be shown as the "key" in an error message when validation applies to the request body as a whole and not its parts?

  • For example, if a POST request expects the request body to be an object, then sending an array will show "data" as the key in both the current implementation and this enhancement.
  • Also note that even if you remove "key": "data", "data" can still show up in error messages. For example, when a request body is required, sending no request body will show "message": "'data'".

Changing the ExtendedMsgSpecValidationError's approach for building its error message is based on the loc_to_dot_sep() example in Pydantic's documentation (https://docs.pydantic.dev/latest/errors/errors/#customize-error-messages). While this change will more closely resemble how error messages are built for ValidationError, I don't know if users prefer "items[1].value" over "items.1.value".

Metadata

Metadata

Assignees

No one assigned

    Labels

    EnhancementThis is a new feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions