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: OpenAPI generation throws exception when using an optional path value #2222

Closed
1 of 4 tasks
smithk86 opened this issue Aug 25, 2023 · 2 comments · Fixed by #2224
Closed
1 of 4 tasks

Bug: OpenAPI generation throws exception when using an optional path value #2222

smithk86 opened this issue Aug 25, 2023 · 2 comments · Fixed by #2224
Assignees
Labels
Bug 🐛 This is something that is not working as expected

Comments

@smithk86
Copy link
Contributor

smithk86 commented Aug 25, 2023

Description

I think I found an OpenAPI generation bug when using multiple paths on the same route with an optional path parameter value. The first call to /schema always fails with a 500 status code. Subsequent calls are successful but do not contain the problem route. I assume this is due to caching.

Another interesting thing I found is that if you load /schema/openapi.json first, you still get a 500 the first time but the cache is built with the route defined for subsequent calls. Although, the parameter is listed as a query parameter instead of a path parameter.

Thanks!
Kyle

URL to code causing the issue

No response

MCVE

from litestar import Litestar, get, MediaType

@get(path=["/", "/{message:str}"], media_type=MediaType.TEXT, sync_to_thread=False)
def route(message: str | None) -> str:
    if message:
        return message
    return "no message"

app = Litestar(route_handlers=[route])

Steps to reproduce

* Run app: litestar run
* Load http://localhost:8000/schema
* Get 500 Internal Server Error

Screenshots

No response

Logs

Traceback (most recent call last):
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/routes/http.py", line 79, in handle
    response = await self._get_response_for_request(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/routes/http.py", line 131, in _get_response_for_request
    response = await self._call_handler_function(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/routes/http.py", line 160, in _call_handler_function
    response_data, cleanup_group = await self._get_response_data(
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/routes/http.py", line 206, in _get_response_data
    data = route_handler.fn.value(**parsed_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/openapi/controller.py", line 197, in root
    return ASGIResponse(body=render_method(request), media_type=MediaType.HTML)
                             ^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/openapi/controller.py", line 357, in render_redoc
    schema = self.get_schema_from_request(request)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/openapi/controller.py", line 85, in get_schema_from_request
    return request.app.openapi_schema
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/app.py", line 562, in openapi_schema
    self.update_openapi_schema()
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/app.py", line 818, in update_openapi_schema
    path_item, created_operation_ids = create_path_item(
                                       ^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/_openapi/path_item.py", line 97, in create_path_item
    create_parameter_for_handler(
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/_openapi/parameters.py", line 214, in create_parameter_for_handler
    for parameter in get_recursive_handler_parameters(
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/_openapi/parameters.py", line 131, in get_recursive_handler_parameters
    create_parameter(
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/_openapi/parameters.py", line 87, in create_parameter
    result = schema_creator.for_field_definition(replace(field_definition, annotation=path_parameter.type))
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/_openapi/schema_generation/schema.py", line 265, in for_field_definition
    result = self.for_optional_field(field_definition)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/_openapi/schema_generation/schema.py", line 306, in for_optional_field
    annotation=make_non_optional_union(field_definition.annotation),
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/utils/typing.py", line 175, in make_non_optional_union
    return cast("UnionT", Union[args])  # pyright: ignore
                          ~~~~~^^^^^^
  File "/home/ksmith/.pyenv/versions/3.11.4/lib/python3.11/typing.py", line 355, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "/home/ksmith/.pyenv/versions/3.11.4/lib/python3.11/typing.py", line 478, in __getitem__
    return self._getitem(self, parameters)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ksmith/.pyenv/versions/3.11.4/lib/python3.11/typing.py", line 685, in Union
    raise TypeError("Cannot take a Union of no types.")
TypeError: Cannot take a Union of no types.
Traceback (most recent call last):
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/middleware/exceptions/middleware.py", line 191, in __call__
    await self.app(scope, receive, send)
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/routes/http.py", line 79, in handle
    response = await self._get_response_for_request(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/routes/http.py", line 131, in _get_response_for_request
    response = await self._call_handler_function(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/routes/http.py", line 160, in _call_handler_function
    response_data, cleanup_group = await self._get_response_data(
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/routes/http.py", line 206, in _get_response_data
    data = route_handler.fn.value(**parsed_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/openapi/controller.py", line 197, in root
    return ASGIResponse(body=render_method(request), media_type=MediaType.HTML)
                             ^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/openapi/controller.py", line 357, in render_redoc
    schema = self.get_schema_from_request(request)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/openapi/controller.py", line 85, in get_schema_from_request
    return request.app.openapi_schema
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/app.py", line 562, in openapi_schema
    self.update_openapi_schema()
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/app.py", line 818, in update_openapi_schema
    path_item, created_operation_ids = create_path_item(
                                       ^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/_openapi/path_item.py", line 97, in create_path_item
    create_parameter_for_handler(
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/_openapi/parameters.py", line 214, in create_parameter_for_handler
    for parameter in get_recursive_handler_parameters(
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/_openapi/parameters.py", line 131, in get_recursive_handler_parameters
    create_parameter(
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/_openapi/parameters.py", line 87, in create_parameter
    result = schema_creator.for_field_definition(replace(field_definition, annotation=path_parameter.type))
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/_openapi/schema_generation/schema.py", line 265, in for_field_definition
    result = self.for_optional_field(field_definition)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/_openapi/schema_generation/schema.py", line 306, in for_optional_field
    annotation=make_non_optional_union(field_definition.annotation),
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/mvce/.venv/lib/python3.11/site-packages/litestar/utils/typing.py", line 175, in make_non_optional_union
    return cast("UnionT", Union[args])  # pyright: ignore
                          ~~~~~^^^^^^
  File "/home/ksmith/.pyenv/versions/3.11.4/lib/python3.11/typing.py", line 355, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "/home/ksmith/.pyenv/versions/3.11.4/lib/python3.11/typing.py", line 478, in __getitem__
    return self._getitem(self, parameters)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ksmith/.pyenv/versions/3.11.4/lib/python3.11/typing.py", line 685, in Union
    raise TypeError("Cannot take a Union of no types.")
TypeError: Cannot take a Union of no types.

Litestar Version

2.0.1

Platform

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

Funding

  • If you would like to see an issue prioritized, make a pledge towards it!
  • We receive the pledge once the issue is completed & verified
Fund with Polar
@smithk86 smithk86 added Bug 🐛 This is something that is not working as expected Triage Required 🏥 This requires triage labels Aug 25, 2023
@ThinksFast
Copy link
Contributor

+1: I noticed this behavior yesterday, I'm also getting a 500 on first page load of the schema paths

@Goldziher
Copy link
Contributor

Can you give a minimum reproduction?

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

Successfully merging a pull request may close this issue.

3 participants