From 5dbfa305673cb1304c78ccae419e7cae5098eab6 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Wed, 16 Jul 2025 15:25:49 +0300 Subject: [PATCH 1/2] fix: Skip idle span creation when app is in background --- packages/core/src/js/tracing/span.ts | 7 ++++ .../test/tracing/idleNavigationSpan.test.ts | 32 +++++++++++++++++++ .../integrations/userInteraction.test.ts | 13 ++++++++ 3 files changed, 52 insertions(+) diff --git a/packages/core/src/js/tracing/span.ts b/packages/core/src/js/tracing/span.ts index ac02dca769..ecb50cda76 100644 --- a/packages/core/src/js/tracing/span.ts +++ b/packages/core/src/js/tracing/span.ts @@ -12,6 +12,7 @@ import { spanToJSON, startIdleSpan as coreStartIdleSpan, } from '@sentry/core'; +import { AppState } from 'react-native'; import { isRootSpan } from '../utils/span'; import { adjustTransactionDuration, cancelInBackground } from './onSpanEndUtils'; @@ -104,6 +105,12 @@ export const startIdleSpan = ( return new SentryNonRecordingSpan(); } + const currentAppState = AppState.currentState; + if (currentAppState === 'background') { + logger.debug(`[startIdleSpan] App is already in background, not starting span for ${startSpanOption.name}`); + return new SentryNonRecordingSpan(); + } + getCurrentScope().setPropagationContext(generatePropagationContext()); const span = coreStartIdleSpan(startSpanOption, { finalTimeout, idleTimeout }); diff --git a/packages/core/test/tracing/idleNavigationSpan.test.ts b/packages/core/test/tracing/idleNavigationSpan.test.ts index ec4ddf7640..74ca24bb7c 100644 --- a/packages/core/test/tracing/idleNavigationSpan.test.ts +++ b/packages/core/test/tracing/idleNavigationSpan.test.ts @@ -35,6 +35,7 @@ describe('startIdleNavigationSpan', () => { jest.useFakeTimers(); NATIVE.enableNative = true; mockedAppState.isAvailable = true; + mockedAppState.currentState = 'active'; mockedAppState.addEventListener = (_, listener) => { mockedAppState.listener = listener; return { @@ -82,6 +83,37 @@ describe('startIdleNavigationSpan', () => { expect(spanToJSON(transaction!).timestamp).toBeDefined(); }); + it('Returns non-recording span when app is already in background', () => { + mockedAppState.currentState = 'background'; + + const span = startIdleNavigationSpan({ + name: 'test', + }); + + // Non-recording spans don't become active + expect(getActiveSpan()).toBeUndefined(); + + // Verify it's a non-recording span + expect(span).toBeDefined(); + expect(span.constructor.name).toBe('SentryNonRecordingSpan'); + + // No AppState listener should be set up for non-recording spans + expect(mockedAppState.removeSubscription).not.toHaveBeenCalled(); + }); + + it('Does not set up AppState listener for background spans', () => { + mockedAppState.currentState = 'background'; + + startIdleNavigationSpan({ + name: 'test', + }); + + mockedAppState.setState('active'); + + // No subscription removal should happen since no listener was set up + expect(mockedAppState.removeSubscription).not.toHaveBeenCalled(); + }); + describe('Start a new active root span (without parent)', () => { it('Starts a new span when there is no active span', () => { const span = startIdleNavigationSpan({ diff --git a/packages/core/test/tracing/integrations/userInteraction.test.ts b/packages/core/test/tracing/integrations/userInteraction.test.ts index a026e79c0e..c152017a63 100644 --- a/packages/core/test/tracing/integrations/userInteraction.test.ts +++ b/packages/core/test/tracing/integrations/userInteraction.test.ts @@ -66,6 +66,7 @@ describe('User Interaction Tracing', () => { jest.useFakeTimers(); NATIVE.enableNative = true; mockedAppState.isAvailable = true; + mockedAppState.currentState = 'active'; mockedAppState.addEventListener = (_, listener) => { mockedAppState.listener = listener; return { @@ -291,5 +292,17 @@ describe('User Interaction Tracing', () => { ); expect(interactionTransactionContext!.timestamp).toBeLessThanOrEqual(routingTransactionContext!.start_timestamp!); }); + + test('does not start UI span when app is in background', () => { + mockedAppState.currentState = 'background'; + + startUserInteractionSpan(mockedUserInteractionId); + + // No active span should be created + expect(getActiveSpan()).toBeUndefined(); + + // No events should be queued + expect(client.eventQueue).toHaveLength(0); + }); }); }); From dfd5c6c54d23318d6992ff8101f97d6ebcc330ff Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Wed, 16 Jul 2025 15:36:39 +0300 Subject: [PATCH 2/2] Add changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9481acc029..75625d7934 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ ## Unreleased +### Fixes + +- Skip idle span creation when app is in background ([#4995](https://github.com/getsentry/sentry-react-native/pull/4995)) + ### Dependencies - Bump JavaScript SDK from v8.54.0 to v8.55.0 ([#4981](https://github.com/getsentry/sentry-react-native/pull/4981))