From ca44ced4b70e6dd7f7dfa5457a2870dbd3c59171 Mon Sep 17 00:00:00 2001 From: Mukher Date: Sat, 29 Aug 2026 12:18:02 +0500 Subject: [PATCH 1/2] Ignore third-party OnyxDB read rejections in Sentry The Convert Experiments script loaded by web/index.html reads OnyxDB directly and rejects with bare strings such as "No data found for key reportActions_undefined". Those rejections carry no stack frames, so thirdPartyErrorFilterIntegration cannot tag them. Match them by message via ignoreErrors instead. --- src/setup/telemetry/setupSentry.ts | 9 +++++-- tests/unit/setupSentryTest.ts | 42 ++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 tests/unit/setupSentryTest.ts diff --git a/src/setup/telemetry/setupSentry.ts b/src/setup/telemetry/setupSentry.ts index e31551b4484d..5e747e290976 100644 --- a/src/setup/telemetry/setupSentry.ts +++ b/src/setup/telemetry/setupSentry.ts @@ -57,8 +57,13 @@ function setupSentry(): void { integrations, environment: CONFIG.ENVIRONMENT, release: `${pkg.name}@${pkg.version}`, - // UPDATE_REQUIRED is not a real error and makes our errors in Spotnana spike and get rate limited when we bump the app min version, so ignore it - ignoreErrors: [CONST.ERROR.UPDATE_REQUIRED], + ignoreErrors: [ + // UPDATE_REQUIRED is not a real error and makes our errors in Spotnana spike and get rate limited when we bump the app min version, so ignore it + CONST.ERROR.UPDATE_REQUIRED, + // Bare-string rejections from the Convert Experiments script in web/index.html, which reads OnyxDB directly. + // They carry no stack frames, so thirdPartyErrorFilterIntegration cannot tag them. + /No data found for key/, + ], denyUrls: EXTENSION_DENY_URLS, beforeSendTransaction: processBeforeSendTransactions, enableLogs: true, diff --git a/tests/unit/setupSentryTest.ts b/tests/unit/setupSentryTest.ts new file mode 100644 index 000000000000..36514e31ef07 --- /dev/null +++ b/tests/unit/setupSentryTest.ts @@ -0,0 +1,42 @@ +import setupSentry from '@src/setup/telemetry/setupSentry'; + +import {stringMatchesSomePattern} from '@sentry/core'; + +jest.mock('@sentry/react-native', () => ({ + init: jest.fn(), + setTag: jest.fn(), +})); + +jest.mock('@libs/telemetry/integrations', () => ({})); + +const sentryMock = jest.requireMock<{init: jest.Mock}]>; setTag: jest.Mock}>('@sentry/react-native'); + +/** How the browser SDK words a promise rejected with a string. It carries no stack frames, so `ignoreErrors` is the only filter that can match it. */ +function rejectionMessage(reason: string): string { + return `Non-Error promise rejection captured with value: ${reason}`; +} + +/** Runs Sentry's own matcher over the patterns setupSentry registered, the way `eventFiltersIntegration` does. */ +function isIgnored(message: string): boolean { + const initOptions = sentryMock.init.mock.calls.at(0)?.at(0); + if (!initOptions) { + throw new Error('setupSentry did not call Sentry.init'); + } + + return stringMatchesSomePattern(message, initOptions.ignoreErrors ?? []); +} + +describe('setupSentry', () => { + beforeEach(() => { + jest.clearAllMocks(); + setupSentry(); + }); + + it('drops the bare-string rejection the Convert Experiments script emits when it reads a missing OnyxDB key', () => { + expect(isIgnored(rejectionMessage('No data found for key reportActions_undefined'))).toBe(true); + }); + + it('keeps unhandled rejections that do not come from that third-party OnyxDB read', () => { + expect(isIgnored(rejectionMessage("undefined is not an object (evaluating 'report.reportID')"))).toBe(false); + }); +}); From e1d885c42a9f7e3838ff868a0f4e43c7cfd4dce7 Mon Sep 17 00:00:00 2001 From: Mukher Date: Sat, 29 Aug 2026 12:33:50 +0500 Subject: [PATCH 2/2] Anchor the ignore pattern to the browser rejection signature The unanchored pattern would also drop a thrown Error whose message happens to contain the same text, on every platform, discarding a real stack. Match the browser SDK's string-rejection wording instead, which still covers every key that helper reads. --- src/setup/telemetry/setupSentry.ts | 5 +++-- tests/unit/setupSentryTest.ts | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/setup/telemetry/setupSentry.ts b/src/setup/telemetry/setupSentry.ts index 5e747e290976..fded8ed2ca86 100644 --- a/src/setup/telemetry/setupSentry.ts +++ b/src/setup/telemetry/setupSentry.ts @@ -61,8 +61,9 @@ function setupSentry(): void { // UPDATE_REQUIRED is not a real error and makes our errors in Spotnana spike and get rate limited when we bump the app min version, so ignore it CONST.ERROR.UPDATE_REQUIRED, // Bare-string rejections from the Convert Experiments script in web/index.html, which reads OnyxDB directly. - // They carry no stack frames, so thirdPartyErrorFilterIntegration cannot tag them. - /No data found for key/, + // They carry no stack frames for thirdPartyErrorFilterIntegration to tag; the prefix limits this to the + // browser SDK's rejection wording, so a real Error carrying the same text still reports. + /^Non-Error promise rejection captured with value: No data found for key/, ], denyUrls: EXTENSION_DENY_URLS, beforeSendTransaction: processBeforeSendTransactions, diff --git a/tests/unit/setupSentryTest.ts b/tests/unit/setupSentryTest.ts index 36514e31ef07..86c261be177e 100644 --- a/tests/unit/setupSentryTest.ts +++ b/tests/unit/setupSentryTest.ts @@ -39,4 +39,8 @@ describe('setupSentry', () => { it('keeps unhandled rejections that do not come from that third-party OnyxDB read', () => { expect(isIgnored(rejectionMessage("undefined is not an object (evaluating 'report.reportID')"))).toBe(false); }); + + it('keeps a thrown Error carrying the same text, which unlike a bare rejection has a stack to act on', () => { + expect(isIgnored('No data found for key reportActions_123')).toBe(false); + }); });