Skip to content

fix(fastapi): preserve OTel context from sync route handlers to BackgroundTasks (#3586)#4724

Open
rajdhruvsingh wants to merge 1 commit into
open-telemetry:mainfrom
rajdhruvsingh:fix/sync-route-background-task-context
Open

fix(fastapi): preserve OTel context from sync route handlers to BackgroundTasks (#3586)#4724
rajdhruvsingh wants to merge 1 commit into
open-telemetry:mainfrom
rajdhruvsingh:fix/sync-route-background-task-context

Conversation

@rajdhruvsingh

Copy link
Copy Markdown

Description

Fixes the context propagation asymmetry described in #3586: when a synchronous
FastAPI route handler attaches a value to the OpenTelemetry context (e.g. via
context.attach(set_value(...))) and then schedules a BackgroundTasks task,
that value was silently lost by the time the background task ran. The same
code in an async route handler worked correctly.

Root cause

Sync route handlers run inside a worker thread via anyio.to_thread.run_sync
(used internally by Starlette/FastAPI). This copies the current context into
the thread, but does not propagate any mutations made inside that thread back
out once it returns. By the time BackgroundTasks.__call__ executes, the
context is already back to its pre-handler state — the mutation is gone before
this instrumentation's existing BackgroundTask.__call__ hook (added for
#4251, background-task span parenting) ever runs, so there's no way to recover
it after the fact.

Fix

BackgroundTasks.add_task is patched to snapshot the current OpenTelemetry
context at the moment a task is scheduled (this happens inside the same
thread as the route handler, so it sees the mutated context). When the task
actually executes, that captured context is merged into the then-current
ambient context — keys already present in the ambient context (notably the
BackgroundTask span this instrumentation starts around each task, from
#4251) take precedence, so existing span-parenting behavior is unaffected.
Only context set during the route handler that would otherwise be lost is
backfilled.

This works identically for sync and async background task functions.

Fixes #3586

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

  • Added test_background_task_inherits_sync_route_context: reproduces the
    exact repro from FastAPI: Context change in sync route does not appear in background tasks #3586 (sync and async routes, each attaching a context
    value before scheduling a background task) and asserts both now see the
    value, where previously only the async case did.

  • Added test_background_task_context_does_not_clobber_span_parenting: a
    sync-route variant of the existing [fastapi] BackgroundTasks produce child spans that outlive their closed parent span #4251 regression test, confirming the
    preserved context does not override the dedicated BackgroundTask span or
    its parent/child span relationships.

  • Verified against the original issue's exact reproduction script
    (TestClient, sync and async endpoints) with the fix applied (both pass)
    and with the fix reverted via git stash (sync fails exactly as reported
    in the issue, async still passes) — confirming the fix addresses the real
    reported behavior rather than an artifact of the new tests.

  • Full existing test suite for opentelemetry-instrumentation-fastapi run
    locally: 101 passed, 6 skipped, 0 failures (2 unrelated pre-existing
    tests deselected — test_entry_point_exists and
    test_instruments_with_fastapi_installed, which fail in any local dev
    .venv with multiple instrumentation packages installed, due to
    entry_points(group="opentelemetry_instrumentor") returning every
    registered instrumentor rather than just this one; this is a known
    environment-isolation issue unrelated to this change and not present
    under the project's isolated tox CI environments).

  • pytest instrumentation/opentelemetry-instrumentation-fastapi/tests/

Does This PR Require a Core Repo Change?

  • Yes. - Link to PR:
  • 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

@rajdhruvsingh rajdhruvsingh requested a review from a team as a code owner June 20, 2026 20:02
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.

FastAPI: Context change in sync route does not appear in background tasks

1 participant