From db77d3271bf2759b89e91b9ef3299dcc5a2628a6 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Wed, 17 Jun 2026 15:42:58 +0200 Subject: [PATCH] fix(core): Forward user geo as an object so the native scope keeps it `NATIVE.setUser` ran every required user key (including `geo`) through `_serializeObject`, which JSON-stringifies non-string values. `geo` therefore crossed the bridge as a JSON string, but the native SDKs deserialize it from a nested map: on Android the `User.Deserializer` threw and the whole user was dropped, on iOS the geo was silently ignored. Forward `geo` as a structured object instead. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 4 ++++ packages/core/src/js/wrapper.ts | 12 ++++++++++-- packages/core/test/wrapper.test.ts | 10 +++++----- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70405f192d..8abe5f1a5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ - Record XHR request/response headers and (optionally) bodies in Mobile Session Replay. Opt in via `mobileReplayIntegration` with `networkDetailAllowUrls` to capture headers; set `networkCaptureBodies: true` to also capture bodies. Other options: `networkDetailDenyUrls`, `networkRequestHeaders`, `networkResponseHeaders`. Authorization-like headers are always stripped, bodies are capped at ~150 KB. Covers XHR-based clients like `axios`; fetch will follow. See [Network Details](https://docs.sentry.io/platforms/react-native/session-replay/#network-details) for details. ([#6288](https://github.com/getsentry/sentry-react-native/pull/6288)) - Warn during dev builds when multiple versions of Sentry JS SDK are detected ([#6269](https://github.com/getsentry/sentry-react-native/pull/6269)) +### Fixes + +- Fix user `geo` being dropped from the native scope by forwarding it as a structured object instead of a JSON string ([#6309](https://github.com/getsentry/sentry-react-native/pull/6309)) + ### Dependencies - Bump Android SDK from v8.43.1 to v8.43.2 ([#6273](https://github.com/getsentry/sentry-react-native/pull/6273)) diff --git a/packages/core/src/js/wrapper.ts b/packages/core/src/js/wrapper.ts index d4225740d6..d38737c803 100644 --- a/packages/core/src/js/wrapper.ts +++ b/packages/core/src/js/wrapper.ts @@ -449,8 +449,8 @@ export const NATIVE: SentryNativeWrapper = { } // separate and serialize all non-default user keys. - let userKeys = null; - let userDataKeys = null; + let userKeys: { [key: string]: unknown } | null = null; + let userDataKeys: { [key: string]: string } | null = null; if (user) { const { id, ip_address, email, username, geo, ...otherKeys } = user; const requiredUser: RequiredKeysUser = { @@ -461,6 +461,14 @@ export const NATIVE: SentryNativeWrapper = { geo, }; userKeys = this._serializeObject(requiredUser); + // `geo` is a structured object that the native SDKs deserialize from a + // nested map. `_serializeObject` JSON-stringifies it like the scalar keys, + // which breaks native deserialization and drops the entire user, so + // overwrite it with the object. See + // https://github.com/getsentry/sentry-react-native/issues/6306 + if (geo !== undefined) { + userKeys.geo = geo; + } userDataKeys = this._serializeObject(otherKeys); } diff --git a/packages/core/test/wrapper.test.ts b/packages/core/test/wrapper.test.ts index 838bf0fbfb..c43dfb84fd 100644 --- a/packages/core/test/wrapper.test.ts +++ b/packages/core/test/wrapper.test.ts @@ -927,11 +927,11 @@ describe('Tests Native Wrapper', () => { id: '123', email: 'test@example.com', username: 'testuser', - geo: JSON.stringify({ + geo: { city: 'San Francisco', country_code: 'US', region: 'California', - }), + }, }, { customField: 'customValue', @@ -951,10 +951,10 @@ describe('Tests Native Wrapper', () => { expect(RNSentry.setUser).toHaveBeenCalledWith( { id: '123', - geo: JSON.stringify({ + geo: { city: 'New York', country_code: 'US', - }), + }, }, {}, ); @@ -969,7 +969,7 @@ describe('Tests Native Wrapper', () => { expect(RNSentry.setUser).toHaveBeenCalledWith( { id: '123', - geo: '{}', + geo: {}, }, {}, );