#6594·ogx

Lift the `fastapi <0.137` cap once route introspection handles `_IncludedRouter` (fastapi ≥ 0.137)

Author: mattfCreated Sep 19, 2026Updated Sep 19, 2026
Labelsdependenciestech-debt

What is the technical debt you think should be addressed?

We currently pin fastapi>=0.115.0,<0.137 in pyproject.toml (resolves to 0.136.3; added while unblocking dependabot PR #6587). That cap is a workaround for a fastapi ≥ 0.137 behavior change, not a real compatibility constraint.

Mechanism: in fastapi ≥ 0.137, APIRouter.include_router() no longer flattens the included router's APIRoutes into the parent's router.routes. Instead it appends a single _IncludedRouter(BaseRoute) wrapper (defined in fastapi/routing.py; constructed at the end of include_router), and the sub-routes are reachable only via .original_router.routes. Verified boundary: 0.136.3 works; 0.140.13 and 0.141.1 both change behavior.

This breaks any code or test that walks router.routes expecting flat APIRoute objects with .path / .methods / .endpoint:

  • Unit tests (verified): tests/unit/core/routers/test_connectors_router.py — 11 of 15 tests fail. The _get_endpoint helper (line 58) reads router.routes off the admin router, the only router built with internal include_router nesting (src/ogx_api/admin/fastapi_routes.py:203-204). The 4 OpenAPI-schema tests still pass (schema generation still resolves nested routes).
  • Server / library-client route introspection (product gap): get_router_routes (src/ogx/core/server/fastapi_router_registry.py:98-100) filters isinstance(route, APIRoute) with no recursion, so it drops the admin router's nested routes. It feeds initialize_route_impls (src/ogx/core/server/routes.py:60, used by the library client at library_client.py:731) and the /admin/inspect/routes + admin route listing (inspect.py:91, admin.py:215). By contrast the server auth path already handles this — build_route_impls_from_routes_collect_api_routes (routes.py:108-119) recurses into .original_router with an explicit "FastAPI >= 0.137" comment — but that adaptation is inconsistent, leaving get_router_routes and metrics.py:94 unhandled.

Because the cap is a hard ceiling, it will keep blocking every future fastapi dependabot bump until the route-introspection code (and the test) are made nesting-aware.

What is the benefit of addressing this technical debt?

  • Restores the ability to track the latest fastapi without a version ceiling, so dependabot python-deps PRs stop needing manual reverts.
  • Makes server / library-client route introspection correct for current and future fastapi (fixes the latent admin-API route drop in get_router_routes).
  • Removes a "magic" version cap that isn't self-explanatory without this issue for context.

Other thoughts

  • The root fix belongs in the route-introspection helpers, not the dependency pin. Make every .routes consumer nesting-aware:
    1. Reimplement get_router_routes on top of the existing recursive _collect_api_routes (or share one helper) so initialize_route_impls, inspect.py, and admin.py all see nested routes.
    2. Check metrics.py:94 (for route in router.routes) for the same flat-router assumption.
    3. Make test_connectors_router.py::_get_endpoint robust to fastapi's router nesting (flatten via original_router, or match on resolved paths) so it doesn't couple to fastapi's internal router.routes shape.
  • Once all .routes consumers + the test are verified against fastapi ≥ 0.137, remove the cap in a single PR.
  • Possibly a good first issue once the intended route-collection behavior is documented.