Bug: Regression - Setting `min_length` on Optional `str` types causes 500 error
Description
I just upgraded from 2.22.0 to 2.24.0 and started seeing this error: TypeError: Can only set min_lengthon a str, bytes, or collection type - typetyping.Annotated[typing.Optional[str], msgspec.Meta(min_length=1, examples=[])] is invalid
This was surprising, because applying meta annotations like this to optional parameters is a core thing we do in many places throughout our projects.
At first I thought this might be a msgspec problem, but it runs just fine outside of litestar, and as far as I can tell the issue only appears when a request is made to a handler that wraps a Struct in litestar's MsgspecDTO.
Instead of checking the exact types for this sort of thing maybe it should be checking whether the sub type is inherited from a valid base type. Or maybe explicitly allow for optional versions of these types.
This worked previously, so I consider it a regression. If this was a purposeful change then I feel like it should wait until a major version bump like 3.0 as it breaks existing code. And if that's the case: what is the canonical way to set a minimum length on an optional string now?
URL to code causing the issue
No response
MCVE
from typing import Annotated
from litestar import Litestar, post
from litestar.dto import MsgspecDTO
from msgspec import Struct, Meta
class TestStruct(Struct):
text: Annotated[str, Meta(min_length=1)] | None
@post('/test', dto=MsgspecDTO[TestStruct])
async def test(data: TestStruct) -> None:
return
app = Litestar(route_handlers=[test])
# then just send a valid POST request, like
# curl --header "Content-Type: application/json" --request POST --data '{"text": "any"}' http://localhost:8000/testSteps to reproduce
Set things like min_length or max_length on optional str, bytes, etc. in a Struct in a MsgspecDTO and then make a valid request to a handler that uses that.
Screenshots
No response
Logs
Traceback (most recent call last):
File "litestar/middleware/_internal/exceptions/middleware.py", line 158, in __call__
await self.app(scope, receive, capture_response_started)
File "litestar/routes/http.py", line 81, in handle
response = await self._get_response_for_request(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "litestar/routes/http.py", line 133, in _get_response_for_request
return await self._call_handler_function(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "litestar/routes/http.py", line 163, in _call_handler_function
kwargs = await parameter_model.to_kwargs(connection=request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "litestar/_kwargs/kwargs_model.py", line 456, in to_kwargs
await extractor(output, connection)
File "litestar/_kwargs/extractors.py", line 491, in extractor
values["data"] = await data_extractor(connection)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "litestar/_kwargs/extractors.py", line 368, in _extract_multipart
return data_dto(connection).decode_builtins(form_values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "litestar/dto/base_dto.py", line 115, in decode_builtins
return backend.populate_data_from_builtins(value, self.asgi_connection)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "litestar/dto/_codegen_backend.py", line 109, in populate_data_from_builtins
return self.transfer_data_from_builtins(self.parse_builtins(builtins, asgi_connection))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "litestar/dto/_backend.py", line 267, in parse_builtins
return convert(
^^^^^^^^
TypeError: Can only set `min_length` on a str, bytes, or collection type - type `typing.Annotated[typing.Optional[str], msgspec.Meta(min_length=1, examples=[])]` is invalidLitestar Version
I confirmed this first appeared in 2.23.0 and still exists in 2.24.0
Platform
- Linux
- Mac
- Windows
- Other (Please specify in the description above)
Source: litestar-org/litestar