Describe the bug
A router likes the following produces an error in redoc.
@router.post("/order/new", response_model=Tuple[bool, str])
Traceback (most recent call last):
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/uvicorn/protocols/http/httptools_impl.py", line 375, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/uvicorn/middleware/message_logger.py", line 58, in __call__
raise exc from None
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/uvicorn/middleware/message_logger.py", line 54, in __call__
await self.app(scope, inner_receive, inner_send)
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/uvicorn/middleware/debug.py", line 81, in __call__
raise exc from None
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/uvicorn/middleware/debug.py", line 78, in __call__
await self.app(scope, receive, inner_send)
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/starlette/applications.py", line 134, in __call__
await self.error_middleware(scope, receive, send)
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/starlette/middleware/errors.py", line 178, in __call__
raise exc from None
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/starlette/middleware/errors.py", line 156, in __call__
await self.app(scope, receive, _send)
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/starlette/middleware/cors.py", line 76, in __call__
await self.app(scope, receive, send)
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/starlette/exceptions.py", line 73, in __call__
raise exc from None
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/starlette/exceptions.py", line 62, in __call__
await self.app(scope, receive, sender)
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/starlette/routing.py", line 590, in __call__
await route(scope, receive, send)
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/starlette/routing.py", line 208, in __call__
await self.app(scope, receive, send)
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/starlette/routing.py", line 41, in app
response = await func(request)
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/fastapi/applications.py", line 94, in openapi
return JSONResponse(self.openapi())
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/fastapi/applications.py", line 86, in openapi
openapi_prefix=self.openapi_prefix,
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/fastapi/openapi/utils.py", line 290, in get_openapi
return jsonable_encoder(OpenAPI(**output), by_alias=True, include_none=False)
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/pydantic/main.py", line 275, in __init__
values, fields_set, _ = validate_model(__pydantic_self__, data)
File "/Users/jcz/Library/Caches/pypoetry/virtualenvs/paperplane-py3.7/lib/python3.7/site-packages/pydantic/main.py", line 785, in validate_model
raise err
pydantic.error_wrappers.ValidationError: 6 validation errors for OpenAPI
content -> application/json -> schema -> items
value is not a valid dict (type=type_error.dict)
content -> application/json -> schema -> items
value is not none (type=type_error.none.allowed)
content -> application/json -> schema -> $ref
field required (type=value_error.missing)
content -> application/json -> schema
value is not none (type=type_error.none.allowed)
paths -> /api/v1/order/new -> post -> responses -> 200 -> content
value is not none (type=type_error.none.allowed)
paths -> /api/v1/order/new -> post
value is not none (type=type_error.none.allowed)
DEBUG: ('127.0.0.1', 56047) - Disconnected
Environment:
I've tried the following:
from typing import Tuple
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class MyModel(BaseModel):
response: Tuple[bool, str] = (True, "It's working :-)")
@app.post("/test", response_model=MyModel)
async def test():
return MyModel()
@app.post("/test2", response_model=Tuple[bool, str])
async def test2():
return (False, "It's not working :-(")
If you comment out the route for test 2, everything works fine.
Otherwise, I get:
File "pydantic/main.py", line 275, in pydantic.main.BaseModel.__init__
File "pydantic/main.py", line 785, in pydantic.main.validate_model
pydantic.error_wrappers.ValidationError: 6 validation errors for OpenAPI
content -> application/json -> schema -> items
value is not a valid dict (type=type_error.dict)
content -> application/json -> schema -> items
value is not none (type=type_error.none.allowed)
content -> application/json -> schema -> $ref
field required (type=value_error.missing)
content -> application/json -> schema
value is not none (type=type_error.none.allowed)
paths -> /test2 -> post -> responses -> 200 -> content
value is not none (type=type_error.none.allowed)
paths -> /test2 -> post
value is not none (type=type_error.none.allowed)
It's worth noting that it is still possible to curl the endpoint, so the issue specifically with the OpenAPI schema creation.
There is a secondary issue... you can break the "/docs" endpoint by adding the above code to tests/test_custom_swagger_ui_redirect.py but the tests still pass in the source code...
This is possibly related to: https://github.com/tiangolo/fastapi/issues/466
I've tried the following:
from typing import Tuple from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class MyModel(BaseModel): response: Tuple[bool, str] = (True, "It's working :-)") @app.post("/test", response_model=MyModel) async def test(): return MyModel() @app.post("/test2", response_model=Tuple[bool, str]) async def test2(): return (False, "It's not working :-(")If you comment out the route for test 2, everything works fine.
Otherwise, I get:
File "pydantic/main.py", line 275, in pydantic.main.BaseModel.__init__ File "pydantic/main.py", line 785, in pydantic.main.validate_model pydantic.error_wrappers.ValidationError: 6 validation errors for OpenAPI content -> application/json -> schema -> items value is not a valid dict (type=type_error.dict) content -> application/json -> schema -> items value is not none (type=type_error.none.allowed) content -> application/json -> schema -> $ref field required (type=value_error.missing) content -> application/json -> schema value is not none (type=type_error.none.allowed) paths -> /test2 -> post -> responses -> 200 -> content value is not none (type=type_error.none.allowed) paths -> /test2 -> post value is not none (type=type_error.none.allowed)It's worth noting that it is still possible to curl the endpoint, so the issue specifically with the OpenAPI schema creation.
Obviously, you try return a tuple, when fastapi is excepting dict.
You could return a tuple and declare it as such, but it's not valid in OpenAPI, so, the docs break.
In OpenAPI you can return a JSON array (a list) of one type (or a union of types), or JSON objects with any types. But you cannot specify an array with a specific sequence of types.
Note that it's valid JSON Schema, but not OpenAPI schema. That's one of the few specific differences.
@tiangolo Thank you very much for the excellent explanation!