Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

### Fixes

- Retry native module resolution to prevent silent event drops in production Hermes builds ([#5981](https://github.com/getsentry/sentry-react-native/pull/5981))
- Lazy-load Metro internal modules to prevent Expo 55 import errors ([#5958](https://github.com/getsentry/sentry-react-native/pull/5958))
- Fix app start transaction profile offset by using the actual profiling start timestamp instead of the adjusted app start time ([#5962](https://github.com/getsentry/sentry-react-native/issues/5962))
- Use React `componentStack` as fallback when error has no stack trace on Android ([#5965](https://github.com/getsentry/sentry-react-native/pull/5965))
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/js/wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function getRNSentryModule(): Spec | undefined {
: NativeModules.RNSentry;
}

const RNSentry: Spec | undefined = getRNSentryModule();
let RNSentry: Spec | undefined = getRNSentryModule();

export interface Screenshot {
data: Uint8Array;
Expand Down Expand Up @@ -649,6 +649,9 @@ export const NATIVE: SentryNativeWrapper = {
},

isNativeAvailable(): boolean {
if (!RNSentry) {
RNSentry = getRNSentryModule();
}
return this._isModuleLoaded(RNSentry);
},

Expand Down
34 changes: 34 additions & 0 deletions packages/core/test/wrapper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1208,4 +1208,38 @@ describe('Tests Native Wrapper', () => {
});
});
});

describe('isNativeAvailable', () => {
test('retries module resolution if initially undefined', () => {
// Simulate the race condition: RNSentry was undefined at module load time
// but becomes available when isNativeAvailable() is called during init()
let mockModule: Spec | undefined = undefined;

jest.resetModules();
jest.doMock('react-native', () => ({
NativeModules: {
get RNSentry() {
return mockModule;
},
},
Platform: { OS: 'ios' },
}));
// Ensure TurboModules path is not used so NativeModules.RNSentry is checked
jest.doMock('../src/js/utils/environment', () => ({
isTurboModuleEnabled: () => false,
}));

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { NATIVE: isolatedNATIVE } = require('../src/js/wrapper');

// Initially unavailable (simulates race condition)
expect(isolatedNATIVE.isNativeAvailable()).toBe(false);

// Native module becomes available (TurboModule registered)
mockModule = RNSentry;

// isNativeAvailable retries and finds it
expect(isolatedNATIVE.isNativeAvailable()).toBe(true);
});
});
});
Loading