Skip to content

Incompatibility with FastAPI 0.137+ / Starlette 1.3.1+ #78

Description

@erikmeier-8451

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:

  1. Install fastapi==0.137.* (or any version pulling Starlette 1.3.1+) and fastapi-versioning==0.10.0.
  2. Create a FastAPI app and register one or more sub-routers using app.include_router(...).
  3. 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}",
    )
  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions