Describe the bug
VersionedFastAPI crashes with AttributeError: '_IncludedRouter' object has no attribute 'endpoint' when used with FastAPI 0.137 (which depends on Starlette 1.3.1+).
In recent versions of Starlette, app.routes can contain _IncludedRouter objects in addition to the usual APIRoute instances. These _IncludedRouter objects are produced when include_router() is called and do not expose an .endpoint attribute. However, fastapi_versioning.versioning.version_to_route unconditionally accesses api_route.endpoint, leading to a hard failure during application startup.
Relevant traceback:
File ".../fastapi_versioning/versioning.py", line 44, in VersionedFastAPI
version_to_route(route, default_version) for route in app.routes
File ".../fastapi_versioning/versioning.py", line 24, in version_to_route
version = getattr(api_route.endpoint, "_api_version", default_version)
^^^^^^^^^^^^^^^^^^
AttributeError: '_IncludedRouter' object has no attribute 'endpoint'
To Reproduce
Steps to reproduce the behavior:
- Install
fastapi==0.137.* (or any version pulling Starlette 1.3.1+) and fastapi-versioning==0.10.0.
- Create a FastAPI app and register one or more sub-routers using
app.include_router(...).
- Wrap the application with
VersionedFastAPI:
from fastapi import APIRouter, FastAPI
from fastapi_versioning import VersionedFastAPI, version
router = APIRouter()
@router.get("/hello")
@version(1)
def hello():
return {"hello": "world"}
base_app = FastAPI()
base_app.include_router(router)
versioned_app = VersionedFastAPI(
base_app,
version_format="{major}",
prefix_format="/api/v{major}",
)
- Run the app (or import the module). The
AttributeError is raised at import/startup time.
Expected behavior
VersionedFastAPI should iterate over app.routes and gracefully handle route entries that are not APIRoute instances (e.g., _IncludedRouter, Mount, WebSocketRoute, etc.), either by:
- Skipping non-
APIRoute entries, or
- Recursing into included routers to discover the actual
APIRoute objects they contain, or
- Preserving them unchanged in the resulting versioned application.
Application startup should succeed when using include_router() together with VersionedFastAPI on modern FastAPI/Starlette versions.
Additional context
fastapi-versioning: 0.10.0
fastapi: 0.137.x
starlette: 1.3.1+
- Python: 3.12
Workaround
As a temporary workaround, filter out non-endpoint route objects from app.router.routes before passing the app to VersionedFastAPI. Note that this drops the routes added via include_router(), so it is only viable if those routes are added directly via decorators or re-registered after wrapping:
# fastapi-versioning 0.10.0 requires every route to have .endpoint;
# starlette 1.3.1+ adds _IncludedRouter objects that don't.
base_app.router.routes = [
r for r in base_app.router.routes if hasattr(r, "endpoint")
]
versioned_app = VersionedFastAPI(
base_app,
version_format="{major}",
prefix_format="/api/v{major}",
)
A proper fix in version_to_route (or its caller in VersionedFastAPI) is needed to restore compatibility with current FastAPI releases.
Describe the bug
VersionedFastAPIcrashes withAttributeError: '_IncludedRouter' object has no attribute 'endpoint'when used with FastAPI 0.137 (which depends on Starlette 1.3.1+).In recent versions of Starlette,
app.routescan contain_IncludedRouterobjects in addition to the usualAPIRouteinstances. These_IncludedRouterobjects are produced wheninclude_router()is called and do not expose an.endpointattribute. However,fastapi_versioning.versioning.version_to_routeunconditionally accessesapi_route.endpoint, leading to a hard failure during application startup.Relevant traceback:
To Reproduce
Steps to reproduce the behavior:
fastapi==0.137.*(or any version pulling Starlette 1.3.1+) andfastapi-versioning==0.10.0.app.include_router(...).VersionedFastAPI:AttributeErroris raised at import/startup time.Expected behavior
VersionedFastAPIshould iterate overapp.routesand gracefully handle route entries that are notAPIRouteinstances (e.g.,_IncludedRouter,Mount,WebSocketRoute, etc.), either by:APIRouteentries, orAPIRouteobjects they contain, orApplication startup should succeed when using
include_router()together withVersionedFastAPIon modern FastAPI/Starlette versions.Additional context
fastapi-versioning: 0.10.0fastapi: 0.137.xstarlette: 1.3.1+Workaround
As a temporary workaround, filter out non-endpoint route objects from
app.router.routesbefore passing the app toVersionedFastAPI. Note that this drops the routes added viainclude_router(), so it is only viable if those routes are added directly via decorators or re-registered after wrapping:A proper fix in
version_to_route(or its caller inVersionedFastAPI) is needed to restore compatibility with current FastAPI releases.