Skip to content

RUM resource tracking silently misses all fetch traffic on Expo SDK 56+ — winter runtime replaces global.fetch with native expo/fetch, bypassing the XHR proxy #1354

Description

@laiskajoonas

Describe the bug

DdRumResourceTracking captures resources by proxying XMLHttpRequest (XHRProxy), relying on React Native's whatwg-fetch polyfill being XHR-backed so that fetch traffic is captured too.

Since Expo SDK 56, Expo's WinterCG runtime installs expo/fetch as global.fetch at startup (expo/src/winter/runtime.native.tsinstall('fetch', () => require('./fetch').fetch), gated only by the EXPO_PUBLIC_USE_RN_FETCH escape hatch). expo/fetch is a native implementation: OkHttp on Android (expo/modules/fetch/ExpoFetchModule.kt, using OkHttpClientProvider.createClient — no Datadog interceptor), URLSession on iOS.

The result is platform-asymmetric and easy to miss:

  • Android: every request made through fetch (e.g. all Apollo/GraphQL traffic) produces no RUM resource events at all. Only libraries that still use XHR directly (e.g. axios) remain visible. No errors, no logs — the events just stop.
  • iOS: resources still appear, but only because the native iOS SDK swizzles URLSession, which happens to cover expo/fetch. They are captured at the native layer rather than through the RN SDK's resource-tracking path (so RN-level features tied to the JS interception, like the resourceEventMapper and first-party-host header injection from JS, no longer apply to them).

Consequences beyond missing timings: @view.network_settled_time on Android is computed from an empty resource set, and Datadog trace-context headers are no longer injected into first-party fetch requests on Android, breaking RUM↔APM correlation.

In our production app the Android resources-per-view rate dropped ~98% (from ~2–3 to ~0.03) the exact release we upgraded Expo 55 → 56, with iOS flat across the same boundary. The surviving Android resources map exactly to our axios (XHR) call sites.

Reproduction steps

  1. Create an Expo SDK 56+ app (bare or CNG) with @datadog/mobile-react-native, RUM enabled with trackResources: true / RumConfiguration(..., trackXHRResources: true).
  2. Make any request with fetch("https://example.com") (or any fetch-based client such as Apollo).
  3. On Android: no resource event is emitted. Adding SdkVerbosity.DEBUG shows startResource is never called.
  4. Confirm the cause: global.fetch is expo/fetch (its module source is expo/src/winter/fetch/fetch.ts, not whatwg-fetch), so XMLHttpRequest is never touched by the request.
  5. Rebuild with the env var EXPO_PUBLIC_USE_RN_FETCH=1 set at bundle time — global.fetch stays RN's XHR-backed polyfill and resource events return. This is our current workaround.

SDK logs

With SdkVerbosity.DEBUG: user actions and long tasks log normally; no Starting RUM Resource lines are ever emitted for fetch traffic on Android.

Expected behavior

Either resource tracking captures expo/fetch traffic (e.g. by wrapping global.fetch/expo/fetch in addition to proxying XHR, or by documenting a supported OkHttp interception path for Expo's client on Android), or the limitation is called out loudly in the docs — the current failure mode is fully silent and, because iOS keeps working via URLSession swizzling, it's easy to attribute the Android gap to anything but the SDK.

Given that Expo has made expo/fetch the default and the escape hatch is explicitly temporary, XHR-only interception effectively means "no fetch resource tracking on current Expo" going forward.

Affected SDK versions

3.4.0–3.5.4 (all current versions; the trigger is the Expo SDK 56+ runtime, not an SDK change)

Latest working SDK version

Any version, when running on Expo SDK ≤ 55 (or with EXPO_PUBLIC_USE_RN_FETCH=1 on 56+)

Did you confirm if the latest SDK version fixes the bug?

Yes (3.5.4 still tracks resources exclusively via the XHR proxy)

Integration Methods

Yarn

React Native Version

0.85.3 (Expo SDK 57; also reproduced on Expo SDK 56 / RN 0.85)

Platform

Android (fully missing), iOS (captured only via native URLSession swizzling, bypassing RN-level resource tracking)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions