Tracer Version(s)
4.10.4
Python Version(s)
3.14
Pip Version(s)
N/A (uv 0.11.19)
Bug Report
Summary
After upgrading FastAPI from 0.136.x to 0.137.x, ddtrace's FastAPI/Starlette integration stops resolving route templates for any endpoint registered via include_router. The http.route tag and the span resource lose the include prefixes; endpoints registered at a router root (empty path) collapse to a bare GET / POST resource with no path at all.
This destroys per-endpoint resource aggregation in APM: every GET across the app lands in a single GET bucket (and likewise POST), so latency/error breakdowns by endpoint and any monitor/dashboard keyed on resource name break.
ddtrace and Starlette versions are held constant in the repro below — only the FastAPI version changes — so this is specifically a FastAPI 0.137 interaction.
Which ddtrace version
Reproduced on ddtrace==4.10.4 (latest stable). The Starlette integration on main (ahead of 4.11.0rc3) is unchanged in the relevant code, so newer releases are expected to behave the same.
Root cause
FastAPI 0.137.0 (PR fastapi/fastapi#15745, "Refactor internals to preserve APIRouter and APIRoute instances") changed APIRouter.include_router. It no longer clones each route onto a single flat parent router; instead it appends a lazy wrapper:
self.routes.append(_IncludedRouter(original_router=router, include_context=include_context))
Potential fix
Note: under 0.137 the leaf route no longer carries the full template — scope["route"].path_format returns only the relative leaf path ('', /{item_id}), so reading scope["route"] is not sufficient.
FastAPI 0.137 does expose the fully-composed template on the request scope. For a matched request, the full template is available at:
scope["fastapi"]["effective_route_context"].path_format
Verified against the repro above:
request /v1/items -> effective path_format = '/v1/items'
request /v1/items/42 -> effective path_format = '/v1/items/{item_id}'
Workaround
Pin fastapi < 0.137 until the integration supports the new include model.
Reproduction Code
Reproduction
import os
os.environ["DD_TRACE_ENABLED"] = "true"
os.environ["DD_TRACE_AGENT_URL"] = "http://localhost:0" # never actually sends
os.environ["DD_INSTRUMENTATION_TELEMETRY_ENABLED"] = "false"
from ddtrace import patch
patch(fastapi=True, starlette=True)
import fastapi
from fastapi import APIRouter, FastAPI
# app.include_router(v1, prefix="/v1") -> v1.include_router(items, prefix="/items")
items = APIRouter()
@items.get("")
def list_items():
return []
@items.get("/{item_id}")
def get_item(item_id: str):
return {"id": item_id}
v1 = APIRouter()
v1.include_router(items, prefix="/items")
app = FastAPI()
app.include_router(v1, prefix="/v1")
# Capture finished spans.
from ddtrace.trace import tracer
captured = []
_writer = tracer._span_aggregator.writer
_orig = _writer.write
_writer.write = lambda spans=None: (captured.extend(spans or []), _orig(spans))[1]
from starlette.testclient import TestClient
with TestClient(app) as client:
client.get("/v1/items")
client.get("/v1/items/42")
print(f"fastapi=={fastapi.__version__}")
for s in captured:
if s.name == "fastapi.request":
print(f" resource={s.resource!r:34} http.route={s.get_tag('http.route')!r}")
Run against each FastAPI version (ddtrace + Starlette held constant):
uv run --no-project --with 'fastapi==0.136.3' --with 'ddtrace==4.10.4' --with 'httpx<0.28' python repro.py
uv run --no-project --with 'fastapi==0.137.1' --with 'ddtrace==4.10.4' --with 'httpx<0.28' python repro.py
Both pull starlette==1.3.1.
Expected (fastapi==0.136.3)
resource='GET /v1/items' http.route='/v1/items'
resource='GET /v1/items/{item_id}' http.route='/v1/items/{item_id}'
Actual (fastapi==0.137.1)
resource='GET ' http.route=''
resource='GET /{item_id}' http.route='/{item_id}'
The collection endpoint loses its path entirely; the item endpoint loses the /v1/items include prefixes.
Error Logs
No response
Libraries in Use
annotated-doc==0.0.4
annotated-types==0.7.0
anyio==4.13.0
bytecode==0.18.1
certifi==2026.5.20
ddtrace==4.10.4
envier==0.6.1
fastapi==0.137.1
h11==0.16.0
httpcore==1.0.9
httpx==0.27.2
idna==3.18
opentelemetry-api==1.42.1
pydantic==2.13.4
pydantic-core==2.46.4
sniffio==1.3.1
starlette==1.3.1
typing-extensions==4.15.0
typing-inspection==0.4.2
wrapt==2.2.1
Operating System
Linux asquin 7.0.11-arch1-1 #1 SMP PREEMPT_DYNAMIC Tue, 02 Jun 2026 18:26:58 +0000 x86_64 GNU/Linux
Tracer Version(s)
4.10.4
Python Version(s)
3.14
Pip Version(s)
N/A (uv 0.11.19)
Bug Report
Summary
After upgrading FastAPI from 0.136.x to 0.137.x, ddtrace's FastAPI/Starlette integration stops resolving route templates for any endpoint registered via
include_router. Thehttp.routetag and the spanresourcelose the include prefixes; endpoints registered at a router root (empty path) collapse to a bareGET/POSTresource with no path at all.This destroys per-endpoint resource aggregation in APM: every
GETacross the app lands in a singleGETbucket (and likewisePOST), so latency/error breakdowns by endpoint and any monitor/dashboard keyed on resource name break.ddtrace and Starlette versions are held constant in the repro below — only the FastAPI version changes — so this is specifically a FastAPI 0.137 interaction.
Which ddtrace version
Reproduced on
ddtrace==4.10.4(latest stable). The Starlette integration onmain(ahead of4.11.0rc3) is unchanged in the relevant code, so newer releases are expected to behave the same.Root cause
FastAPI 0.137.0 (PR fastapi/fastapi#15745, "Refactor internals to preserve
APIRouterandAPIRouteinstances") changedAPIRouter.include_router. It no longer clones each route onto a single flat parent router; instead it appends a lazy wrapper:Potential fix
Note: under 0.137 the leaf route no longer carries the full template —
scope["route"].path_formatreturns only the relative leaf path ('',/{item_id}), so readingscope["route"]is not sufficient.FastAPI 0.137 does expose the fully-composed template on the request scope. For a matched request, the full template is available at:
Verified against the repro above:
Workaround
Pin
fastapi < 0.137until the integration supports the new include model.Reproduction Code
Reproduction
Run against each FastAPI version (ddtrace + Starlette held constant):
Both pull
starlette==1.3.1.Expected (fastapi==0.136.3)
Actual (fastapi==0.137.1)
The collection endpoint loses its path entirely; the item endpoint loses the
/v1/itemsinclude prefixes.Error Logs
No response
Libraries in Use
annotated-doc==0.0.4
annotated-types==0.7.0
anyio==4.13.0
bytecode==0.18.1
certifi==2026.5.20
ddtrace==4.10.4
envier==0.6.1
fastapi==0.137.1
h11==0.16.0
httpcore==1.0.9
httpx==0.27.2
idna==3.18
opentelemetry-api==1.42.1
pydantic==2.13.4
pydantic-core==2.46.4
sniffio==1.3.1
starlette==1.3.1
typing-extensions==4.15.0
typing-inspection==0.4.2
wrapt==2.2.1
Operating System
Linux asquin 7.0.11-arch1-1 #1 SMP PREEMPT_DYNAMIC Tue, 02 Jun 2026 18:26:58 +0000 x86_64 GNU/Linux