Skip to content

fix(fastapi): fix resource name for starlette and fastapi#18653

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 18 commits into
mainfrom
mohammad/fix_resource_name_for_fastapi_v0.137
Jun 24, 2026
Merged

fix(fastapi): fix resource name for starlette and fastapi#18653
gh-worker-dd-mergequeue-cf854d[bot] merged 18 commits into
mainfrom
mohammad/fix_resource_name_for_fastapi_v0.137

Conversation

@chojomok

@chojomok chojomok commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Description

FastAPI 0.137 (PR #15745) refactored APIRouter.include_router to use a lazy _IncludedRouter wrapper instead of flattening routes onto the parent router at registration time. This introduced two regressions in the ddtrace starlette/fastapi integration:

  1. Missing path prefixscope["route"].path_format now returns only the leaf-relative segment (e.g. "" or /{item_id}) instead of the fully-composed template (e.g. /v1/items). Spans produced by nested include_router calls lose their prefix entirely, collapsing all GET requests into a single GET bucket in APM.

  2. Path doublingAPIRoute.handle in 0.137 falls back to super().handle() (starlette Route.handle) when no effective_route_context is present. When both the fastapi and starlette patches are active, traced_handler fires twice for the same route instance, doubling the accumulated path (e.g. GET /file/file).

Fix in ddtrace/contrib/internal/starlette/patch.py (traced_handler):

  • effective_route_context override: FastAPI 0.137 sets scope["fastapi"]["effective_route_context"].path_format to the fully-composed route template before calling APIRoute.handle. When this key is present, use it directly as the resource name instead of relying on path accumulation from instance.path.

  • Seen-routes guard: Track route instances processed per request via scope["datadog"]["_dd_seen_routes"]. If traced_handler is entered for an instance already seen in this request, skip accumulation and pass through — preventing the double-counting introduced by the super().handle() fallback.

The lockfile (162f3ce) is bumped to fastapi==0.137.1 / starlette==1.3.1 so the latest venv in CI exercises the affected versions.

Testing

Risks

  • The _dd_seen_routes guard uses id(instance) to deduplicate route calls within a request. id() values can be reused after GC, but route objects are singletons created at app startup and live for the full application lifetime, so collision is not possible in practice.
  • The effective_route_context key is FastAPI-specific (scope["fastapi"]) and is guarded with .get() so it is a no-op on pure Starlette and older FastAPI versions.

Design Decision: Seen-Routes Guard

The conceptually cleaner fix for path doubling would be to skip patching starlette.routing.Route.handle entirely when the fastapi patch is active, letting the fastapi patch own APIRoute.handle exclusively. This was rejected because starlette apps mounted inside FastAPI go through Route.handle without going through APIRoute.handle — removing the starlette patch on Route.handle would break resource names for those sub-apps. Tracking seen route instances per request is the safer, more targeted approach.

Additional Notes

  • The _dd_seen_routes guard is necessary independently of the effective_route_context fix: the former addresses double-firing on plain routes when both patches are active; the latter addresses missing prefixes on nested include_router routes. Both are required — verified by running each change in isolation.
  • Older FastAPI versions (0.64, 0.86, 0.90) do not set scope["fastapi"] or call super().handle() in APIRoute.handle, so neither change affects their behaviour.

… name truncation in FastAPI >= 0.137

FastAPI 0.137 changed include_router to use a lazy _IncludedRouter wrapper,
causing scope["route"].path_format to return only the leaf-relative path.
This breaks resource names for nested routers (e.g. "GET " instead of
"GET /v1/items"). Bump the latest lockfile to 0.137.1 to surface the failure
in CI and add a targeted test for the missing-prefix case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Jun 17, 2026

Copy link
Copy Markdown

Codeowners resolved as

.riot/requirements/162f3ce.txt                                          @DataDog/apm-python
ddtrace/contrib/internal/starlette/patch.py                             @DataDog/apm-core-python @DataDog/apm-idm-python
releasenotes/notes/fix-fastapi-starlette-resource-name-0137-98a92f6730889b54.yaml  @DataDog/apm-python
scripts/integration_registry/registry.yaml                              @DataDog/apm-core-python @DataDog/apm-idm-python
supported_versions_output.json                                          @DataDog/apm-core-python @DataDog/apm-idm-python
supported_versions_table.csv                                            @DataDog/apm-core-python @DataDog/apm-idm-python
tests/contrib/fastapi/test_fastapi.py                                   @DataDog/apm-core-python @DataDog/apm-idm-python

@datadog-datadog-prod-us1

datadog-datadog-prod-us1 Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Pipelines  Tests

Fix all issues with BitsAI

⚠️ Warnings

🚦 8 Pipeline jobs failed

DataDog/apm-reliability/dd-trace-py | build linux serverless: [arm64, cp315-cp315, v113741589-d2b8243-musllinux_1_2_aarch64, 1]   View in Datadog   GitLab

DataDog/apm-reliability/dd-trace-py | build linux: [arm64, cp315-cp315, v113741357-d2b8243-manylinux2014_aarch64]   View in Datadog   GitLab

DataDog/apm-reliability/dd-trace-py | build linux: [arm64, cp315-cp315, v113741589-d2b8243-musllinux_1_2_aarch64]   View in Datadog   GitLab

View all 8 failed jobs.

ℹ️ Info

No other issues found (see more)

🧪 All tests passed
❄️ No new flaky tests detected

🔄 Datadog auto-retried 1 job - 1 passed on retry View in Datadog

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 725c5fe | Docs | Datadog PR Page | Give us feedback!

@chojomok chojomok added the changelog/no-changelog A changelog entry is not required for this PR. label Jun 17, 2026
@pr-commenter

pr-commenter Bot commented Jun 17, 2026

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-06-23 16:02:06

Comparing candidate commit 725c5fe in PR branch mohammad/fix_resource_name_for_fastapi_v0.137 with baseline commit 44d41ec in branch main.

Found 0 performance improvements and 3 performance regressions! Performance is the same for 617 metrics, 10 unstable metrics.

scenario:iastaspects-strip_aspect

  • 🟥 execution_time [+59.126µs; +64.520µs] or [+18.738%; +20.447%]

scenario:iastaspectsospath-ospathbasename_aspect

  • 🟥 execution_time [+99.848µs; +106.544µs] or [+23.786%; +25.381%]

scenario:span-start

  • 🟥 execution_time [+1.367ms; +1.545ms] or [+8.855%; +10.012%]

@chojomok chojomok changed the title test(fastapi): add regression test for nested include_router resource… test(fastapi): fix resource name for starlette and fastapi Jun 17, 2026
chojomok and others added 5 commits June 17, 2026 14:23
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove dead else branch — effective_route_context is only set for HTTP
routes which always have a method in scope.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@chojomok chojomok marked this pull request as ready for review June 17, 2026 20:11
@chojomok chojomok requested review from a team as code owners June 17, 2026 20:11

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9595a1f594

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ddtrace/contrib/internal/starlette/patch.py
chojomok and others added 4 commits June 17, 2026 16:46
…ctor helpers

Merge _build_resource_path into _get_fastapi_effective_path so the function
returns the fully-composed path (with mount prefix) or None in one step.

When FastAPI is mounted inside a Starlette app, Starlette's Mount copies
the scope before delegating so accumulated resource_paths prefixes are lost.
Fall back to scope["root_path"] which Starlette sets on the copied scope.

Adds test_mounted_fastapi_resource_names to cover this case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The mounted FastAPI resource name fix relies on effective_route_context
which only exists in FastAPI >= 0.137. Skip on older versions where the
behavior was already broken before this fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The mounted FastAPI-inside-Starlette scenario was already broken before
this PR. Remove the test and the root_path fallback since they address
a pre-existing issue outside the scope of this fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…gle responsibility

Remove mount_prefix logic and resource_paths parameter — the function
just returns the effective path from FastAPI's scope or None.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@chojomok chojomok changed the title test(fastapi): fix resource name for starlette and fastapi fix(fastapi): fix resource name for starlette and fastapi Jun 18, 2026
@datadog-dd-trace-py-rkomorn

This comment has been minimized.

@chojomok chojomok removed the changelog/no-changelog A changelog entry is not required for this PR. label Jun 22, 2026
Comment thread releasenotes/notes/fix-fastapi-starlette-resource-name-0137-98a92f6730889b54.yaml Outdated

@mabdinur mabdinur left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall the fix is correct and well-structured. A few minor things inline.

Comment thread ddtrace/contrib/internal/starlette/patch.py Outdated
Comment thread ddtrace/contrib/internal/starlette/patch.py
Comment thread ddtrace/contrib/internal/starlette/patch.py
- Consolidate and simplify release note
- Add AIDEV-NOTE to _is_duplicate_route_call documenting id() assumption
- Guard resource_paths accumulation to skip dead state in FastAPI >= 0.137 path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@dubloom dubloom left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may over focusing on comments but I think it is an important part of keeping the code readable (having there at the right places with only the right information to allow a dev to go efficiently around the code)

Comment thread ddtrace/contrib/internal/starlette/patch.py Outdated
Comment thread releasenotes/notes/fix-fastapi-starlette-resource-name-0137-98a92f6730889b54.yaml Outdated
@gh-worker-dd-mergequeue-cf854d gh-worker-dd-mergequeue-cf854d Bot merged commit ee467fa into main Jun 24, 2026
1252 checks passed
@gh-worker-dd-mergequeue-cf854d gh-worker-dd-mergequeue-cf854d Bot deleted the mohammad/fix_resource_name_for_fastapi_v0.137 branch June 24, 2026 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants