Skip to content

fix(fastapi): support instrumenting middleware-wrapped applications#4729

Open
kshitizj03 wants to merge 3 commits into
open-telemetry:mainfrom
kshitizj03:fix/4031-fastapi-middleware-wrapped-app
Open

fix(fastapi): support instrumenting middleware-wrapped applications#4729
kshitizj03 wants to merge 3 commits into
open-telemetry:mainfrom
kshitizj03:fix/4031-fastapi-middleware-wrapped-app

Conversation

@kshitizj03

Copy link
Copy Markdown
Contributor

Description

FastAPIInstrumentor.instrument_app/uninstrument_app assumed they were handed a fastapi.FastAPI instance and raised AttributeError: 'CORSMiddleware' object has no attribute 'build_middleware_stack' when given an application wrapped in ASGI middleware, e.g. CORSMiddleware(app=fastapi_app).

This adds a _unwrap_middleware helper that follows the conventional .app attribute chain down to the underlying application, and skips gracefully with a warning when no app is found. The public app parameter annotation is widened to ASGIApp to reflect that wrapped apps are now accepted.

Note: the unwrap stop-condition and guards check Starlette rather than fastapi.FastAPI. fastapi.FastAPI is monkeypatched to the _InstrumentedFastAPI subclass while global instrumentation is active, so gating on fastapi.FastAPI would make pre-existing app instances fail the isinstance check during that window.

Builds on the approach and review feedback from the now-stale #4041 (@punitmahes), with @xrmx's notes applied (stop unwrapping at the app instance; changelog under Fixed) and the parameter typing he flagged addressed.

Fixes #4031

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

  • Added an integration test instrumenting a CORSMiddleware-wrapped app and asserting theemitted SERVER span name and http.route attribute (name and value type).
  • Added unit tests for _unwrap_middleware (bare app, single/nested middleware, FastAPIinstance with an unrelated app attribute, and the no-app-found case) and for the skip path when no FastAPI application is found.
  • Ran the full opentelemetry-instrumentation-fastapi suite against both the oldest(fastapi==0.119.1/starlette==0.48.0) and latest (fastapi==0.137.2/starlette==1.3.1) pinned versions, all passing. ruff check and ruff format --check clean.

Does This PR Require a Core Repo Change?

  • No.

Checklist:

See contributing.md for styleguide, changelog guidelines, and more.

  • Followed the style guidelines of this project
  • Changelogs have been updated
  • Unit tests have been added
  • Documentation has been updated

@kshitizj03 kshitizj03 requested a review from a team as a code owner June 22, 2026 19:50
kshitizj03 added a commit to kshitizj03/opentelemetry-python-contrib that referenced this pull request Jun 22, 2026
towncrier uses the fragment filename as the changelog reference, so name
it after the PR (open-telemetry#4729) rather than the issue.

Assisted-by: Claude Opus 4.8
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

This PR has been automatically marked as stale because it has not had any activity for 14 days. It will be closed if no further activity occurs within 14 days of this comment.
If you're still working on this, please add a comment or push new commits.

@github-actions github-actions Bot added the Stale label Jul 7, 2026
FastAPIInstrumentor.instrument_app/uninstrument_app assumed they were
handed a fastapi.FastAPI instance and raised AttributeError when given an
application wrapped in ASGI middleware such as
CORSMiddleware(app=fastapi_app), because the wrapper has no
build_middleware_stack method.

Add a _unwrap_middleware helper that follows the conventional `.app`
attribute chain down to the underlying Starlette/FastAPI application, and
skip gracefully with a warning when none is found. Widen the public app
parameter annotation to ASGIApp to reflect that wrapped apps are now
accepted. The unwrap stop-condition and guards check Starlette rather
than fastapi.FastAPI because the latter is monkeypatched to a subclass
while global instrumentation is active, which would make pre-existing app
instances fail an isinstance check.

Builds on the approach and maintainer review feedback from the
previously abandoned PR open-telemetry#4041 by @punitmahes.

Fixes open-telemetry#4031

Assisted-by: Claude Opus 4.8
towncrier uses the fragment filename as the changelog reference, so name
it after the PR (open-telemetry#4729) rather than the issue.

Assisted-by: Claude Opus 4.8
The non-FastAPI uninstrument test referenced no instance state, tripping
pylint R6301 (no-self-use) in CI. Assert that uninstrument_app on a
non-Starlette app does not attach _original_build_middleware_stack, which
both uses self and strengthens the test.

Assisted-by: Claude Opus 4.8
@kshitizj03 kshitizj03 force-pushed the fix/4031-fastapi-middleware-wrapped-app branch from 14af6a2 to a16ebd4 Compare July 7, 2026 05:33
@kshitizj03

Copy link
Copy Markdown
Contributor Author

Rebased on latest main. CI is green and the PR is mergeable. The underlying issue (#4031) is still open and reproducible: instrument_app() fails on a FastAPI app wrapped in CORSMiddleware (or any ASGI middleware) because the app isn't unwrapped before instrumentation. This PR adds a small _unwrap_middleware helper that follows the middleware .app chain down to the underlying Starlette/FastAPI instance, with unit + integration tests.

@xrmx would you be able to take a look when you have a moment? It revives the approach from #4041 with your earlier review comments applied.

@github-actions github-actions Bot removed the Stale label Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Incompatible with CORSMiddleware app

1 participant