Skip to content

fix(instrumentation-fetch, instrumentation-xml-http-request)!: remove unreachable CORS Preflight span - #7025

Open
nabeelamjadsheikh wants to merge 1 commit into
open-telemetry:mainfrom
nabeelamjadsheikh:fix/5122-remove-cors-preflight-spans
Open

fix(instrumentation-fetch, instrumentation-xml-http-request)!: remove unreachable CORS Preflight span#7025
nabeelamjadsheikh wants to merge 1 commit into
open-telemetry:mainfrom
nabeelamjadsheikh:fix/5122-remove-cors-preflight-spans

Conversation

@nabeelamjadsheikh

@nabeelamjadsheikh nabeelamjadsheikh commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Fixes #5122

What

Removes the CORS Preflight span from @opentelemetry/instrumentation-fetch and @opentelemetry/instrumentation-xml-http-request.

Both instrumentations carried an _addChildSpan helper that started a CORS Preflight child span whenever getResource() came back with a corsPreFlightRequest:

const corsPreFlightRequest = resource.corsPreFlightRequest;
if (corsPreFlightRequest) {
  this._addChildSpan(span, corsPreFlightRequest);
  this._markResourceAsUsed(corsPreFlightRequest);
}

That branch cannot be taken. getResource() derives corsPreFlightRequest by splitting two PerformanceResourceTiming entries inside the span's time window, but under Resource Timing Level 2 a preflight is not reported as its own entry — it is folded into the main request's entry, alongside redirects and authentication challenges.

Prior art

This is the follow-up @pichlermarc invited when closing #5130 ("please open a new PR if this is incorrect"). Context from that thread:

So the runtime removal was never actually landed — #5130 was closed on the understanding it had been, when only the fetch tests had.

Verification

The premise was asserted from the spec in the original thread but never demonstrated, so I measured it. Two local origins, an endpoint that requires a preflight (custom request header), and both round trips deliberately delayed 300 ms so a separate entry would be trivially visible if one existed:

browser:               Chrome/151.0.7922.170
server saw methods:    ["OPTIONS","GET"]     <- preflight genuinely sent
getEntriesByName len:  1
resource entries len:  1

fetch initiator:

{ "initiatorType": "fetch", "fetchStart": 43.7, "requestStart": 347.6,
  "responseStart": 649.6, "responseEnd": 649.9, "duration": 606.2 }

xmlhttprequest initiator:

{ "initiatorType": "xmlhttprequest", "fetchStart": 40.4, "requestStart": 345.1,
  "responseStart": 647.4, "responseEnd": 647.7, "duration": 607.3 }

One entry in both cases, and the ~304 ms gap between fetchStart and requestStart is the preflight — absorbed into the single entry rather than reported beside it, with duration spanning both round trips. There is no second entry for the split to find, and no way to recover the preflight's timing from what is exposed.

This also matches the note already sitting in the fetch test suite, which points at this same issue:

https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts#L951-L961

since preflight requests are completely transparent, the instrumentation code could not detect that it happened, let alone report on its timing

Changes

  • instrumentation-fetch: drop _addChildSpan and its call site.
  • instrumentation-xml-http-request: drop _addChildSpan and its call site, plus the now-unused PerformanceTimingNames as PTN import.
  • xhr.test.ts: delete the 10 preflight assertions and re-index the exportSpy.args[...] lookups that were offset by the preflight span being exported first. (These tests fabricate two mock resource entries, which is why they exercised a shape browsers do not emit.) fetch.test.ts needs no changes — [instrumentation-fetch] Use msw for fetch instrumentation tests #5282 already removed its preflight assertions.
  • Changelog entry under Breaking Changes.

Test results:

  • instrumentation-xml-http-request: Executed 124 of 136 (skipped 12) SUCCESS — the 12 skips are the pre-existing sync-mode abort/timeout cases.
  • instrumentation-fetch: Executed 103 of 103 SUCCESS — unchanged.
  • lint and prettier --check clean for both packages.

Scope — narrower than #5130, deliberately

#5130 also stripped corsPreFlightRequest and findMainRequest() out of @opentelemetry/sdk-trace-web. I've left that package untouched here, because PerformanceResourceTimingInfo is public API of a stable package and removing a field from it can't land outside a major. Keeping it also means the main-request selection behaviour is unchanged — the main span still takes its network events from mainRequest exactly as before, so this PR is purely a removal of spans that were never emitted.

Happy to follow up with a deprecation on corsPreFlightRequest to start that clock, if maintainers want it.

One behavioural note for reviewers

Alongside the span, this also drops this._markResourceAsUsed(corsPreFlightRequest). If a split ever did occur, the preflight entry would no longer be marked and could be matched against a later span. The measurement above says that can't happen, but I'd rather flag it than bury it — if you'd prefer the marking kept defensively, say the word and I'll restore it.

… unreachable CORS Preflight span

Both browser instrumentations contained an `_addChildSpan` helper that
emitted a `CORS Preflight` child span whenever `getResource()` reported a
`corsPreFlightRequest`.

Under Resource Timing Level 2 the preflight request is folded into the
main request's `PerformanceResourceTiming` entry instead of being
surfaced as a separate one, so `performance.getEntriesByType('resource')`
never yields the extra entry the split relies on. The branch is therefore
unreachable in any browser implementing the current spec, and no `CORS
Preflight` span has been produced in practice.

Removes the dead helper and its call site from both instrumentations,
along with the XHR tests that asserted on the span. `getResource()` in
`@opentelemetry/sdk-trace-web` still returns `corsPreFlightRequest`; that
field is part of the stable package's public API and is left untouched.

Refs: open-telemetry#5122
Signed-off-by: nabeelamjadsheikh <131901574+nabeelamjadsheikh@users.noreply.github.com>
@nabeelamjadsheikh
nabeelamjadsheikh requested review from a team as code owners August 24, 2026 08:50
@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Aug 24, 2026

Copy link
Copy Markdown

Pull request dashboard status

Waiting on reviewers · refreshed 2026-09-02 16:40 UTC

Review the latest changes.

Status above doesn't look right?
  • Just replied or pushed? Anything around or after the refresh time above may not be picked up yet — give it a few minutes.
  • Anything look wrong? Report it with what you expected; it helps us improve the dashboard.

@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.02%. Comparing base (2bc214d) to head (635e2e6).
⚠️ Report is 7 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7025      +/-   ##
==========================================
+ Coverage   94.98%   95.02%   +0.03%     
==========================================
  Files         409      409              
  Lines       14335    14318      -17     
  Branches     3276     3274       -2     
==========================================
- Hits        13616    13605      -11     
+ Misses        719      713       -6     
Files with missing lines Coverage Δ
...s/opentelemetry-instrumentation-fetch/src/fetch.ts 85.43% <ø> (+2.25%) ⬆️
...emetry-instrumentation-xml-http-request/src/xhr.ts 92.95% <ø> (-0.27%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@overbalance overbalance added the browser Browser-specific additions or benefits label Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

browser Browser-specific additions or benefits

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CORS Preflight Requests not accounted for

2 participants