From 483fac662f3b0fa6e96197765d963fc4577d5d3c Mon Sep 17 00:00:00 2001 From: Trancever Date: Wed, 7 Oct 2026 10:05:17 +0200 Subject: [PATCH 1/4] fix(core): preserve Hermes internal bytecode frames in Expo --- CHANGELOG.md | 2 + .../core/src/js/integrations/rewriteframes.ts | 11 ++-- .../test/integrations/rewriteframes.test.ts | 58 +++++++++++++++++++ 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 564bdbe63b..3d00d27551 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ ### Fixes +- Preserve Hermes internal bytecode frames as non-application frames when rewriting Expo stack traces. + - Fix stale `turbo_module.*` tags on Android native crashes ([#6823](https://github.com/getsentry/sentry-react-native/pull/6823)) - Forward image attachments to Spotlight instead of stripping them from the envelope ([#6818](https://github.com/getsentry/sentry-react-native/pull/6818)) - Don't crash the iOS app when Sentry is initialized with an invalid DSN ([#6825](https://github.com/getsentry/sentry-react-native/pull/6825)) diff --git a/packages/core/src/js/integrations/rewriteframes.ts b/packages/core/src/js/integrations/rewriteframes.ts index 22bdb475ce..5d81062815 100644 --- a/packages/core/src/js/integrations/rewriteframes.ts +++ b/packages/core/src/js/integrations/rewriteframes.ts @@ -46,6 +46,13 @@ export function createReactNativeRewriteFrames(): Integration { frame.colno += 1; } + // Hermes runtime bytecode is separate from the Expo application bundle. + if (frame.filename === 'InternalBytecode.js' || frame.filename === '/InternalBytecode.js') { + frame.in_app = false; + frame.filename = 'app:///InternalBytecode.js'; + return frame; + } + // Expo adds hash to the end of bundle names if (isExpo() && Platform.OS === 'android') { frame.filename = ANDROID_DEFAULT_BUNDLE_NAME; @@ -58,10 +65,6 @@ export function createReactNativeRewriteFrames(): Integration { } const appPrefix = 'app://'; - // https://github.com/getsentry/sentry-react-native/issues/3348 - if (frame.filename === '/InternalBytecode.js') { - frame.in_app = false; - } // We always want to have a triple slash frame.filename = frame.filename.indexOf('/') === 0 ? `${appPrefix}${frame.filename}` : `${appPrefix}/${frame.filename}`; diff --git a/packages/core/test/integrations/rewriteframes.test.ts b/packages/core/test/integrations/rewriteframes.test.ts index 6bc9825328..7c0a2aab5f 100644 --- a/packages/core/test/integrations/rewriteframes.test.ts +++ b/packages/core/test/integrations/rewriteframes.test.ts @@ -865,6 +865,64 @@ describe('RewriteFrames', () => { }); }); + describe.each([ + { platform: 'ios' as const, expo: true, bundle: 'app:///main.jsbundle' }, + { platform: 'android' as const, expo: true, bundle: 'app:///index.android.bundle' }, + { platform: 'ios' as const, expo: false, bundle: 'app:///entry.hbc' }, + { platform: 'android' as const, expo: false, bundle: 'app:///entry.hbc' }, + ])('Hermes on $platform with Expo=$expo', ({ platform, expo, bundle }) => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + it.each(['InternalBytecode.js', '/InternalBytecode.js', '/build/hermes/lib/InternalBytecode/InternalBytecode.js'])( + 'keeps %s frames separate from the application bundle', + async filename => { + mockFunction(isExpo).mockReturnValue(expo); + mockFunction(isHermesEnabled).mockReturnValue(true); + jest.replaceProperty(Platform, 'OS', platform); + + const error = { + message: 'Network request failed', + name: 'Error', + stack: + 'Error: Network request failed\n' + + ' at fetchToken (address at /updates/entry.hbc:1:4344175)\n' + + ` at tryCallOne (address at ${filename}:1:1180)\n` + + ` at anonymous (address at ${filename}:1:1874)\n` + + ' at apply (native)', + }; + + const exception = await exceptionFromError(error); + + expect(exception?.stacktrace?.frames).toEqual([ + { filename: 'native', function: 'apply', in_app: true }, + { + filename: 'app:///InternalBytecode.js', + function: 'anonymous', + lineno: 1, + colno: 1875, + in_app: false, + }, + { + filename: 'app:///InternalBytecode.js', + function: 'tryCallOne', + lineno: 1, + colno: 1181, + in_app: false, + }, + { + filename: bundle, + function: 'fetchToken', + lineno: 1, + colno: 4344176, + in_app: true, + }, + ]); + }, + ); + }); + it('InternalBytecode should be flaged as not InApp', async () => { mockFunction(isHermesEnabled).mockReturnValue(true); From 1bbaf28fe70fb1676fb1dfc1022235bad761fb37 Mon Sep 17 00:00:00 2001 From: Trancever Date: Wed, 7 Oct 2026 17:00:21 +0200 Subject: [PATCH 2/4] docs(core): link Hermes fix in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d00d27551..7a8d3ae9c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ ### Fixes -- Preserve Hermes internal bytecode frames as non-application frames when rewriting Expo stack traces. +- Preserve Hermes internal bytecode frames as non-application frames when rewriting Expo stack traces ([#6848](https://github.com/getsentry/sentry-react-native/pull/6848)). - Fix stale `turbo_module.*` tags on Android native crashes ([#6823](https://github.com/getsentry/sentry-react-native/pull/6823)) - Forward image attachments to Spotlight instead of stripping them from the envelope ([#6818](https://github.com/getsentry/sentry-react-native/pull/6818)) From eeae3bbdc968c9f2cb6fd5d2fd7ac277bab3ab42 Mon Sep 17 00:00:00 2001 From: Dawid Urbaniak Date: Thu, 8 Oct 2026 11:10:01 +0200 Subject: [PATCH 3/4] Apply suggestion from @Trancever --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a8d3ae9c0..3436b93acd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,6 @@ ### Fixes - Preserve Hermes internal bytecode frames as non-application frames when rewriting Expo stack traces ([#6848](https://github.com/getsentry/sentry-react-native/pull/6848)). - - Fix stale `turbo_module.*` tags on Android native crashes ([#6823](https://github.com/getsentry/sentry-react-native/pull/6823)) - Forward image attachments to Spotlight instead of stripping them from the envelope ([#6818](https://github.com/getsentry/sentry-react-native/pull/6818)) - Don't crash the iOS app when Sentry is initialized with an invalid DSN ([#6825](https://github.com/getsentry/sentry-react-native/pull/6825)) From ffd4ece40a04d1e35fe1a74612042bdd76fc9cb2 Mon Sep 17 00:00:00 2001 From: Dawid Urbaniak Date: Thu, 8 Oct 2026 11:10:22 +0200 Subject: [PATCH 4/4] Apply suggestion from @Trancever --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3436b93acd..9dcec8a5fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ ### Fixes -- Preserve Hermes internal bytecode frames as non-application frames when rewriting Expo stack traces ([#6848](https://github.com/getsentry/sentry-react-native/pull/6848)). +- Preserve Hermes internal bytecode frames as non-application frames when rewriting Expo stack traces ([#6848](https://github.com/getsentry/sentry-react-native/pull/6848)) - Fix stale `turbo_module.*` tags on Android native crashes ([#6823](https://github.com/getsentry/sentry-react-native/pull/6823)) - Forward image attachments to Spotlight instead of stripping them from the envelope ([#6818](https://github.com/getsentry/sentry-react-native/pull/6818)) - Don't crash the iOS app when Sentry is initialized with an invalid DSN ([#6825](https://github.com/getsentry/sentry-react-native/pull/6825))