Package versions
- fastapi-filter: 3.0.0
- fastapi: 0.141.1
- pydantic: 2.13.4
- starlette: 1.3.1
- Python: 3.10
Summary
FilterDepends can emit example: {} in the generated OpenAPI schema for optional query parameters declared with FastAPI Query(...) metadata, e.g. Field(Query(default=None)).
Swagger UI treats example: {} as an actual example value. For optional boolean filters, this can make Swagger send or validate {} instead of leaving the parameter unset, resulting in errors like Value must be a boolean.
Minimal reproduction
from typing import Annotated
from fastapi import FastAPI, Query
from fastapi_filter import FilterDepends
from fastapi_filter.contrib.sqlalchemy import Filter
from pydantic import Field
from pydantic.json_schema import SkipJsonSchema
class User:
is_discharged = None
class UserFilter(Filter):
is_discharged: bool | SkipJsonSchema[None] = Field(Query(default=None))
class Constants(Filter.Constants):
model = User
app = FastAPI()
@app.get("/users")
def list_users(filters: Annotated[UserFilter, FilterDepends(UserFilter)]):
return {}
schema = app.openapi()
params = schema["paths"]["/users"]["get"]["parameters"]
print(params)
Actual behavior
The generated OpenAPI parameter contains an empty example:
{
"name": "is_discharged",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{"type": "boolean"},
{"type": "null"}
],
"title": "Is Discharged"
},
"example": {}
}
Expected behavior
No example key should be emitted when the original FastAPI Query example was unset.
Explicit examples should still be preserved:
Field(Query(default=None, example=True))
should still emit:
"example": true
Suspected cause
fastapi-filter deep-copies Pydantic field metadata in _list_to_str_fields:
field_info = deepcopy(f)
FastAPI represents an unset example with the _Unset DefaultPlaceholder sentinel. In FastAPI 0.141.1, deep-copying that sentinel produces a new DefaultPlaceholder object:
from copy import deepcopy
from fastapi.params import _Unset
copied = deepcopy(_Unset)
print(copied is _Unset) # False
print(copied == _Unset) # True
FastAPI's OpenAPI generation checks the sentinel by identity, so the copied placeholder is no longer recognized as unset and gets serialized as {}.
Why this matters
This appears to be a schema/documentation bug rather than a filter parsing bug. Runtime filtering can still work, but the generated OpenAPI schema causes Swagger UI to present/send invalid examples for optional filter params.
Possible fix
When creating the generated filter model, normalize copied FastAPI Param/Query metadata so copied unset example placeholders are restored to FastAPI's real _Unset sentinel, or avoid deep-copying that specific sentinel.
The fix should preserve:
- explicit non-empty example
- examples
- aliases
- descriptions
- defaults
- validators
- current list-to-string behavior
It should only remove/reset unset or empty-object examples caused by the metadata copy.
Package versions
Summary
FilterDependscan emitexample: {}in the generated OpenAPI schema for optional query parameters declared with FastAPIQuery(...)metadata, e.g.Field(Query(default=None)).Swagger UI treats
example: {}as an actual example value. For optional boolean filters, this can make Swagger send or validate{}instead of leaving the parameter unset, resulting in errors likeValue must be a boolean.Minimal reproduction
Actual behavior
The generated OpenAPI parameter contains an empty example:
{ "name": "is_discharged", "in": "query", "required": false, "schema": { "anyOf": [ {"type": "boolean"}, {"type": "null"} ], "title": "Is Discharged" }, "example": {} }Expected behavior
No example key should be emitted when the original FastAPI Query example was unset.
Explicit examples should still be preserved:
Field(Query(default=None, example=True))should still emit:
"example": trueSuspected cause
fastapi-filter deep-copies Pydantic field metadata in _list_to_str_fields:
field_info = deepcopy(f)FastAPI represents an unset example with the _Unset DefaultPlaceholder sentinel. In FastAPI 0.141.1, deep-copying that sentinel produces a new DefaultPlaceholder object:
FastAPI's OpenAPI generation checks the sentinel by identity, so the copied placeholder is no longer recognized as unset and gets serialized as {}.
Why this matters
This appears to be a schema/documentation bug rather than a filter parsing bug. Runtime filtering can still work, but the generated OpenAPI schema causes Swagger UI to present/send invalid examples for optional filter params.
Possible fix
When creating the generated filter model, normalize copied FastAPI Param/Query metadata so copied unset example placeholders are restored to FastAPI's real
_Unsetsentinel, or avoid deep-copying that specific sentinel.The fix should preserve:
It should only remove/reset unset or empty-object examples caused by the metadata copy.