-
First Check
Commit to Help
Example Codefrom enum import StrEnum
from fastapi import FastAPI
from pydantic import BaseModel, Field
class MessageEventType(StrEnum):
alpha = "alpha"
beta = "beta"
class MessageEvent(BaseModel):
event_type: MessageEventType = Field(default=MessageEventType.alpha)
output: str
class MessageOutput(BaseModel):
body: str = ""
events: list[MessageEvent]
class Message(BaseModel):
id: str
input: str
output: MessageOutput
app = FastAPI(title="Minimal FastAPI App", version="1.0.0")
@app.post("/messages", response_model=Message)
async def create_message(input_message: str) -> Message:
return Message(
input=input_message,
output=MessageOutput(body=f"Processed: {input_message}"),
)
if __name__ == "__main__":
openapi_spec = app.openapi()
for c in list(openapi_spec.get("components", {}).get("schemas", {}).keys()):
print(c)DescriptionIssue SummaryTake the minimal example provided above, run with Now run the same code with Note how Minor TweakOne minor tweak to the example and this issue goes away. Remove the enum from class MessageEvent(BaseModel):
output: strRun again and the issue is no longer there. TLDRWhy does the presence of an enum value on Operating SystemmacOS Operating System Details15.6.1 (24G90) FastAPI Version0.119.0 Pydantic Version2.12.0 Python VersionPython 3.13.1 Additional ContextNo response |
Beta Was this translation helpful? Give feedback.
Replies: 10 comments 11 replies
-
|
We are seeing the same issue: starting with |
Beta Was this translation helpful? Give feedback.
-
|
We're seeing the same issue of input/output splitting, along with another issue: "measurement_property": {
- "$ref": "#/components/schemas/MeasurementProperty"
+ "$ref": "#/components/schemas/backend__presentation__api__rest__mrv__resources__measurement_sample__MeasurementProperty"
},We don't really want to expose our file structure in our OpenAPI spec and have to update it every time we move code around. My assumption is that it's to do with this removal of a schema generator that defines a template for refs https://github.com/fastapi/fastapi/pull/14168/files#diff-0412b2399fafac2b9e40381e59f625416679188081bef717068f8ae6005dad77L513 - schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)in #14168 |
Beta Was this translation helpful? Give feedback.
-
|
Same here. |
Beta Was this translation helpful? Give feedback.
-
|
For me this happens in two different cases:
Output: |
Beta Was this translation helpful? Give feedback.
-
|
Seeing this same issue, on version 0.118.3 these schema models would output as 1 and after upgrading to 0.119 they are split into from typing import Literal, Optional, NamedTuple, Union
from fastapi import FastAPI, APIRouter
from pydantic import BaseModel, ConfigDict
from pydantic.alias_generators import to_camel
class CamelModel(BaseModel):
model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
Position2D = NamedTuple("Position2D", [("longitude", float), ("latitude", float)])
Position3D = NamedTuple(
"Position3D", [("longitude", float), ("latitude", float), ("altitude", float)]
)
Position = Union[Position2D, Position3D]
class Point(CamelModel):
type: Literal["Point"]
coordinates: Position
class ExampleModel(CamelModel):
id: int
name: str
location: Optional[Point]
class ExampleResponse(CamelModel):
results: list[ExampleModel]
total_results: int
class ExampleResponseTwo(CamelModel):
results: list[ExampleModel]
router = APIRouter()
@router.get('/example-1', response_model=ExampleResponse)
async def example_1() -> ExampleResponse:
return ExampleResponse(
results=[
ExampleModel(
id=1,
name="Test Example 1",
)
],
total_results=1
)
@router.get('/example-2', response_model=ExampleResponseTwo)
def example_2() -> ExampleResponseTwo:
return ExampleResponseTwo(
results=[
ExampleModel(
id=2,
name="Test Example 2",
)
],
)
def create_fastapi_app():
fast_api = FastAPI()
fast_api.include_router(router)
return fast_api
app = create_fastapi_app()
if __name__ == "__main__":
openapi_spec = app.openapi()
for schema_model in list(openapi_spec.get("components", {}).get("schemas", {}).keys()):
print(schema_model) |
Beta Was this translation helpful? Give feedback.
-
|
In addition to all the above reported, we are ALSO seeing non-deterministic schema names generated starting with E.g. a |
Beta Was this translation helpful? Give feedback.
-
|
same here, breaking a lot of clients auto generated based on spec adding suffix after upgrade to 0.119.0 |
Beta Was this translation helpful? Give feedback.
-
|
This regression is now tied into a CVE in starlette, as we cannot update fastapi because of this problem, but fastapi 0.118.3 (the last without this problem) does not allow starlette >= 0.49.1, the version where the CVE is fixed... |
Beta Was this translation helpful? Give feedback.
-
|
Thank you all for the discussion! 🙌 ☕ I added an issue here: #14247 just for completeness. It should be fixed in #14246, just relased in FastAPI 0.120.2. 🎉 Thanks for the great report @walsha2, I used it with some minor tweaks as the test for the fix PR. 😎 |
Beta Was this translation helpful? Give feedback.
-
|
@tiangolo Unfortunately I have the same issue with non-deterministic schema. I've got several models that appear in multiple files with the same name and definition:
and when I run: app = fastapi.FastAPI(...)
app.include_router(...)
openapi_schema = app.openapi()I get a non-deterministic schema where it was previously deterministic: diff --git a/mobile/generated/types/openapi.json b/mobile/generated/types/openapi.json
index 00bd2bf2..6afd7d06 100644
--- a/mobile/generated/types/openapi.json
+++ b/mobile/generated/types/openapi.json
@@ -2958,7 +2958,7 @@
"title": "Ends At"
},
"widget": {
- "$ref": "#/components/schemas/api__client__location__WidgetSimple"
+ "$ref": "#/components/schemas/api__client__other__WidgetSimple"
},
"location": {
"$ref": "#/components/schemas/api__client__other__LocationDetail"
@@ -3192,12 +3192,12 @@
"title": "Radius Feet"
},
"widget_instructions": {
- "$ref": "#/components/schemas/api__client__location__InstructionDetail"
+ "$ref": "#/components/schemas/api__other__widget__InstructionDetail"
},
"acme_instructions": {
"anyOf": [
{
- "$ref": "#/components/schemas/api__client__location__InstructionDetail"
+ "$ref": "#/components/schemas/api__other__widget__InstructionDetail"
},
{
"type": "null"
...Now I can work around this by just sharing types or by using globally unique names, but I'd prefer not to have to worry about managing either of those. The goal is to explicitly NOT share types so that different APIs can change their shape independently without causing breakage to other unrelated APIs. I'm happy to provide access to the code as well (I tried to repro with a VERY basic set up and was not able to get a minimal repro with about 15 minutes of effort. Sorry!) |
Beta Was this translation helpful? Give feedback.
Thank you all for the discussion! 🙌 ☕
I added an issue here: #14247 just for completeness.
It should be fixed in #14246, just relased in FastAPI 0.120.2. 🎉
Thanks for the great report @walsha2, I used it with some minor tweaks as the test for the fix PR. 😎