Skip to content

Bug: PydanticDTO fails to serialize Pydantic v2 AwareDatetime #4502

@MetaHG

Description

@MetaHG

Description

When using Pydantic v2 with PydanticDTO, fields typed as pydantic.AwareDatetime are not correctly serialized. They should be treated as strings during the DTO transfer phase, but they are currently missing from the _down_types mapping.

URL to code causing the issue

No response

MCVE

from datetime import UTC, datetime

from litestar import post
from litestar.plugins.pydantic import PydanticDTO
from litestar.testing import create_test_client
from pydantic import AwareDatetime, BaseModel


class MyModel(BaseModel):
    dt: AwareDatetime


class MyDTO(PydanticDTO[MyModel]):
    pass


@post("/incoming", dto=MyDTO, sync_to_thread=False)
def incoming_handler(data: MyModel) -> None:
    pass


@post("/outgoing", dto=MyDTO, sync_to_thread=False)
def outgoing_handler() -> MyModel:
    return MyModel(dt=datetime.now(tz=UTC))


if __name__ == "__main__":
    with create_test_client(route_handlers=[incoming_handler, outgoing_handler]) as client:
        data = MyModel(dt=datetime.now(tz=UTC))
        response = client.post("/outgoing")  # Works fine
        response = client.post("/incoming", content=data.model_dump_json())  # Fails

Steps to reproduce

  1. Create a Pydantic v2 model with AwareDatetime.
  2. Use a handler with PydanticDTO.
  3. Send payload with expected data for DTO.

Screenshots

No response

Logs

INFO - 2025-11-28 18:20:26,753 - httpx - _client - HTTP Request: POST http://testserver.local/outgoing "HTTP/1.1 201 Created"
ERROR - 2025-11-28 18:20:26,755 - litestar - config - Uncaught exception (connection_type=http, path='/incoming'):
Traceback (most recent call last):
  File "toy/.venv/lib/python3.13/site-packages/litestar/serialization/msgspec_hooks.py", line 139, in default_deserializer
    raise TypeError(f"Unsupported type: {type(value)!r}")
TypeError: Unsupported type: <class 'str'>

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "toy/.venv/lib/python3.13/site-packages/litestar/serialization/msgspec_hooks.py", line 209, in decode_json
    return msgspec.json.decode(
           ~~~~~~~~~~~~~~~~~~~^
        value,
        ^^^^^^
    ...<5 lines>...
        strict=strict,
        ^^^^^^^^^^^^^^
    )
    ^
msgspec.ValidationError: Unsupported type: <class 'str'> - at `$.dt`

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "toy/.venv/lib/python3.13/site-packages/litestar/routes/http.py", line 163, in _call_handler_function
    kwargs = await parameter_model.to_kwargs(connection=request)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "toy/.venv/lib/python3.13/site-packages/litestar/_kwargs/kwargs_model.py", line 386, in to_kwargs
    await extractor(output, connection)
  File "toy/.venv/lib/python3.13/site-packages/litestar/_kwargs/extractors.py", line 486, in extractor
    values["data"] = await data_extractor(connection)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "toy/.venv/lib/python3.13/site-packages/litestar/_kwargs/extractors.py", line 504, in dto_extractor
    return data_dto(connection).decode_bytes(body)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "toy/.venv/lib/python3.13/site-packages/litestar/plugins/pydantic/dto.py", line 104, in decode_bytes
    return super().decode_bytes(value)
           ~~~~~~~~~~~~~~~~~~~~^^^^^^^
  File "toy/.venv/lib/python3.13/site-packages/litestar/dto/base_dto.py", line 120, in decode_bytes
    return backend.populate_data_from_raw(value, self.asgi_connection)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "toy/.venv/lib/python3.13/site-packages/litestar/dto/_codegen_backend.py", line 137, in populate_data_from_raw
    return self._transfer_to_model_type(self.parse_raw(raw, asgi_connection))
                                        ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
  File "toy/.venv/lib/python3.13/site-packages/litestar/dto/_backend.py", line 244, in parse_raw
    result = decode_json(value=raw, target_type=self.annotation, type_decoders=type_decoders, strict=False)
  File "toy/.venv/lib/python3.13/site-packages/litestar/serialization/msgspec_hooks.py", line 219, in decode_json
    raise SerializationException(str(msgspec_error)) from msgspec_error
litestar.exceptions.base_exceptions.SerializationException: Unsupported type: <class 'str'> - at `$.dt`

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "toy/.venv/lib/python3.13/site-packages/litestar/middleware/_internal/exceptions/middleware.py", line 158, in __call__
    await self.app(scope, receive, capture_response_started)
  File "toy/.venv/lib/python3.13/site-packages/litestar/_asgi/asgi_router.py", line 100, in __call__
    await asgi_app(scope, receive, send)
  File "toy/.venv/lib/python3.13/site-packages/litestar/routes/http.py", line 81, in handle
    response = await self._get_response_for_request(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        scope=scope, request=request, route_handler=route_handler, parameter_model=parameter_model
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "toy/.venv/lib/python3.13/site-packages/litestar/routes/http.py", line 133, in _get_response_for_request
    return await self._call_handler_function(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        scope=scope, request=request, parameter_model=parameter_model, route_handler=route_handler
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "toy/.venv/lib/python3.13/site-packages/litestar/routes/http.py", line 165, in _call_handler_function
    raise ClientException(str(e)) from e
litestar.exceptions.http_exceptions.ClientException: 400: Unsupported type: <class 'str'> - at `$.dt`
INFO - 2025-11-28 18:20:26,756 - httpx - _client - HTTP Request: POST http://testserver.local/incoming "HTTP/1.1 400 Bad Request"

Litestar Version

Litestar 2.18.0
Python 3.13.0

Platform

  • Linux
  • Mac
  • Windows
  • Other (Please specify in the description above)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Bug 🐛This is something that is not working as expected

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions