Describe the bug:
Starting with FastAPI 0.137, the ElasticAPM Starlette/FastAPI middleware crashes with an AttributeError while resolving the transaction (route) name on every HTTP request.
FastAPI 0.137 (fastapi/fastapi#15785) changed how included routers are represented in app.routes. Instead of flattening sub-routers, router.routes now contains _IncludedRouter wrapper objects (the new iter_route_contexts() API is the supported way to walk them). These _IncludedRouter objects do not expose a .path attribute
The agent's _get_route_name() iterates over app.routes and unconditionally reads route.path:
def _get_route_name(self, scope, routes, route_name=None):
for route in routes:
match, child_scope = route.matches(scope)
if match == Match.FULL:
route_name = route.path # <-- _IncludedRouter has no `path`
...
elif match == Match.PARTIAL and route_name is None:
route_name = route.path # <-- same here
Because route.path is treated as non-nullable / always-present, the loop raises AttributeError: '_IncludedRouter' object has no attribute 'path', and every request returns 500 Internal Server Error.
This is the same root cause that broke prometheus-fastapi-instrumentator and OpenTelemetry, e.g. vllm-project/vllm#45597 and open-telemetry/opentelemetry-python-contrib#4700.
Traceback
INFO: 10.224.234.6:40760 - "POST /RPC2 HTTP/1.1" 500 Internal Server Error
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/app/.venv/lib/python3.14/site-packages/uvicorn/protocols/http/h11_impl.py", line 415, in run_asgi
result = await app( # type: ignore[func-returns-value]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self.scope, self.receive, self.send
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/app/.venv/lib/python3.14/site-packages/uvicorn/middleware/proxy_headers.py", line 56, in __call__
return await self.app(scope, receive, send)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/.venv/lib/python3.14/site-packages/fastapi/applications.py", line 1162, in __call__
await super().__call__(scope, receive, send)
File "/app/.venv/lib/python3.14/site-packages/starlette/applications.py", line 90, in __call__
await self.middleware_stack(scope, receive, send)
File "/app/.venv/lib/python3.14/site-packages/elasticapm/instrumentation/packages/asyncio/starlette.py", line 48, in call
return await wrapped(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/.venv/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
raise exc
File "/app/.venv/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
await self.app(scope, receive, _send)
File "/app/.venv/lib/python3.14/site-packages/elasticapm/contrib/starlette/__init__.py", line 194, in __call__
await self._request_started(request)
File "/app/.venv/lib/python3.14/site-packages/elasticapm/contrib/starlette/__init__.py", line 244, in _request_started
transaction_name = self.get_route_name(request) or request.url.path
~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/app/.venv/lib/python3.14/site-packages/elasticapm/contrib/starlette/__init__.py", line 251, in get_route_name
route_name = self._get_route_name(scope, routes)
File "/app/.venv/lib/python3.14/site-packages/elasticapm/contrib/starlette/__init__.py", line 274, in _get_route_name
route_name = route.path
^^^^^^^^^^
AttributeError: '_IncludedRouter' object has no attribute 'path'
To Reproduce
- Install
fastapi>=0.137 and elastic-apm.
- Create a FastAPI app that uses an included router (
app.include_router(...)), so app.routes contains an _IncludedRouter entry.
- Add the middleware:
app.add_middleware(ElasticAPM, client=apm).
- Send any request -> the request fails with
500 and the traceback above is logged from elasticapm/contrib/starlette/__init__.py in _get_route_name / get_route_name.
Environment
- OS: Linux (Docker,
python:3.14)
- Python version: 3.14
- Framework and version: FastAPI 0.137 (Starlette); served via Uvicorn (h11)
- APM Server version: N/A (fails before sending data)
- Agent version: elastic-apm (current)
Describe the bug:
Starting with FastAPI 0.137, the
ElasticAPMStarlette/FastAPI middleware crashes with anAttributeErrorwhile resolving the transaction (route) name on every HTTP request.FastAPI 0.137 (fastapi/fastapi#15785) changed how included routers are represented in
app.routes. Instead of flattening sub-routers,router.routesnow contains_IncludedRouterwrapper objects (the newiter_route_contexts()API is the supported way to walk them). These_IncludedRouterobjects do not expose a.pathattributeThe agent's
_get_route_name()iterates overapp.routesand unconditionally readsroute.path:apm-agent-python/elasticapm/contrib/starlette/__init__.py
Line 250 in 4505c92
Because
route.pathis treated as non-nullable / always-present, the loop raisesAttributeError: '_IncludedRouter' object has no attribute 'path', and every request returns500 Internal Server Error.This is the same root cause that broke
prometheus-fastapi-instrumentatorand OpenTelemetry, e.g. vllm-project/vllm#45597 and open-telemetry/opentelemetry-python-contrib#4700.Traceback
To Reproduce
fastapi>=0.137andelastic-apm.app.include_router(...)), soapp.routescontains an_IncludedRouterentry.app.add_middleware(ElasticAPM, client=apm).500and the traceback above is logged fromelasticapm/contrib/starlette/__init__.pyin_get_route_name/get_route_name.Environment
python:3.14)