Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: return_dto with optional nested Struct field raises 500 error when no default exists #3785

Open
1 of 4 tasks
bdoms opened this issue Oct 13, 2024 · 0 comments
Open
1 of 4 tasks
Labels
Bug 🐛 This is something that is not working as expected

Comments

@bdoms
Copy link

bdoms commented Oct 13, 2024

Description

I read through every open bug and couldn't find something directly comparable (though in general it seems like nested objects in DTOs is a recurring source of issues).

The crux is that I don't want to provide a default for the None case of a field because I want to explicitly set it, even if it's None. That works fine if the field is a simple type like str but when it's a nested type like a Struct of its own then an error appears stating TypeError: Missing required argument even when the argument is provided.

URL to code causing the issue

No response

MCVE

from litestar import Litestar, get
from litestar.dto import MsgspecDTO
from msgspec import Struct


class Nested(Struct):
    item: str


class IWorkOut(Struct):
    foo: Nested | None = None

@get('/works', return_dto=MsgspecDTO[IWorkOut])
async def works() -> IWorkOut:
    return IWorkOut(foo=None)


class IFail(Struct):
    # the only difference here vs `IWorkOut` above is not including `= None`
    # also note that changing `Nested` to a simple type like `str` here makes the error go away
    foo: Nested | None

@get('/fails', return_dto=MsgspecDTO[IFail])
async def fails() -> IFail:
    return IFail(foo=None)


app = Litestar([works, fails])


Worth noting: I'm reporting this here and not with Msgspec because just running `IFail(foo=None)` by itself works correctly. The issue appears to only occur because of the `return_dto`.

Steps to reproduce

1. Run the app, I use uvicorn: `uvicorn main:app`
2. `GET /works` and it correctly returns a 200 with `{"foo": null}`
3. `GET /fails` and it returns a 500 with `{"status_code":500,"detail":"Internal Server Error"}`
4. See error logging below

Screenshots

No response

Logs

INFO:     127.0.0.1:49410 - "GET /fails HTTP/1.1" 500 Internal Server Error
ERROR - 2024-10-12 21:39:00,401 - root - example - Missing required argument 'foo'
Traceback (most recent call last):
  File "[...]/litestar/middleware/_internal/exceptions/middleware.py", line 159, in __call__
    await self.app(scope, receive, capture_response_started)
  File "[...]/litestar/_asgi/asgi_router.py", line 100, in __call__
    await asgi_app(scope, receive, send)
  File "[...]/litestar/routes/http.py", line 80, in handle
    response = await self._get_response_for_request(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "[...]/litestar/routes/http.py", line 132, in _get_response_for_request
    return await self._call_handler_function(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "[...]/litestar/routes/http.py", line 156, in _call_handler_function
    response: ASGIApp = await route_handler.to_response(app=scope["app"], data=response_data, request=request)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "[...]/litestar/handlers/http_handlers/base.py", line 555, in to_response
    data = return_dto_type(request).data_to_encodable_type(data)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "[...]/litestar/dto/base_dto.py", line 119, in data_to_encodable_type
    return backend.encode_data(data)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "[...]/litestar/dto/_codegen_backend.py", line 161, in encode_data
    return cast("LitestarEncodableType", self._encode_data(data))
                                         ^^^^^^^^^^^^^^^^^^^^^^^
  File "<string>", line 33, in func
TypeError: Missing required argument 'foo'

Litestar Version

2.12.1

Platform

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

Note

While we are open for sponsoring on GitHub Sponsors and
OpenCollective, we also utilize Polar.sh to engage in pledge-based sponsorship.

Check out all issues funded or available for funding on our Polar.sh dashboard

  • If you would like to see an issue prioritized, make a pledge towards it!
  • We receive the pledge once the issue is completed & verified
  • This, along with engagement in the community, helps us know which features are a priority to our users.
Fund with Polar
@bdoms bdoms added the Bug 🐛 This is something that is not working as expected label Oct 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug 🐛 This is something that is not working as expected
Projects
None yet
Development

No branches or pull requests

1 participant