Description
After updating to RNGH v3 we started getting crashes in our production Android build, with:
minifyEnabled true
shrinkResources true
The crash is similar to:
Debug builds are fine, only the minified release build crashes. Logs pointed us to globalThis._RNGH_MODULE_ID and to how RNGestureHandlerModule gets initialized on Android.
What we found
_RNGH_MODULE_ID is set on the JSI global from C++, in RNGHRuntimeDecorator::installRNRuntimeBindings:
// shared/runtime/RNGHRuntimeDecorator.cpp:87-89
auto moduleIdValue = jsi::Value(moduleId);
rnRuntime.global().setProperty(rnRuntime, "_RNGH_MODULE_ID", std::move(moduleIdValue));
On Android this runs from the bindings installer returned by the Kotlin module (android/src/main/jni/RNGestureHandlerModule.cpp:34-42), so it happens only after RNGestureHandlerModule is constructed.
TurboModules are lazy, and as far as I can see the only thing that asks for this one is the module scope call in the codegen spec:
// src/specs/NativeRNGestureHandlerModule.ts:40
export default TurboModuleRegistry.getEnforcing<Spec>('RNGestureHandlerModule');
And the root view reads the global directly at render:
// src/components/GestureHandlerRootView.android.tsx:21
moduleId={globalThis._RNGH_MODULE_ID} // Ensure moduleId is set
So the component depends on some other module having imported the spec first. I could not find a place where this order is actually guaranteed, but maybe I am looking in the wrong place and there is something that ensures it that I missed.
If the global is still undefined at that moment, codegen applies the WithDefault<Int32, -1> default, native gets -1, and then:
// android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.kt:30
RNGestureHandlerModule.registries[moduleId] ?: throw Exception("Tried to access a non-existent registry")
Our theory for the release build is that with a lot of JS and more work during startup, plus minification and resource shrinking, the module initialization does not finish before the first render of GestureHandlerRootView. We could not confirm the exact timing, so this part is still a guess.
Environment
react-native-gesture-handler: 3.3.0
react-native: 0.87.1
react: 19.2.3
- Platform: Android
- Architecture: new arch (
newArchEnabled=true)
- JS engine: Hermes
- Fails with
minifyEnabled true + shrinkResources true in release
Steps to reproduce
To make it visible in a clean project without minification, we import GestureHandlerRootView directly by its path instead of from the package root:
import * as React from 'react';
import { Text } from 'react-native';
import GestureHandlerRootView from 'react-native-gesture-handler/lib/module/components/GestureHandlerRootView';
export default function App() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<Text>hello</Text>
</GestureHandlerRootView>
);
}
On Android this crashes every time, also in debug.
I know deep import is not public API and nobody should write this in a real app. We use the normal import in our app. It is only a way to show the same end state in a simple example, because it removes everything else that the package root pulls in.
The import graph shows that lib/module/index.js has NativeRNGestureHandlerModule, however in some cases it's somehow skipped or removed, which could cause an issue with GestureHandlerRootView.
A link to a Gist, an Expo Snack or a link to a repository based on this template that reproduces the bug.
https://github.com/ArturKalach/rngh3-android-module-id-repro
Gesture Handler version
3.3.0
React Native version
0.87.1
Platforms
Android
JavaScript runtime
Hermes
Workflow
React Native (without Expo)
Architecture
New Architecture (Fabric)
Build type
Release mode
Device
Android emulator
Device model
Android Emulator, Real Device
Acknowledgements
Yes
Description
After updating to RNGH v3 we started getting crashes in our production Android build, with:
The crash is similar to:
Debug builds are fine, only the minified release build crashes. Logs pointed us to
globalThis._RNGH_MODULE_IDand to howRNGestureHandlerModulegets initialized on Android.What we found
_RNGH_MODULE_IDis set on the JSI global from C++, inRNGHRuntimeDecorator::installRNRuntimeBindings:On Android this runs from the bindings installer returned by the Kotlin module (
android/src/main/jni/RNGestureHandlerModule.cpp:34-42), so it happens only afterRNGestureHandlerModuleis constructed.TurboModules are lazy, and as far as I can see the only thing that asks for this one is the module scope call in the codegen spec:
And the root view reads the global directly at render:
So the component depends on some other module having imported the spec first. I could not find a place where this order is actually guaranteed, but maybe I am looking in the wrong place and there is something that ensures it that I missed.
If the global is still
undefinedat that moment, codegen applies theWithDefault<Int32, -1>default, native gets-1, and then:Our theory for the release build is that with a lot of JS and more work during startup, plus minification and resource shrinking, the module initialization does not finish before the first render of
GestureHandlerRootView. We could not confirm the exact timing, so this part is still a guess.Environment
react-native-gesture-handler: 3.3.0react-native: 0.87.1react: 19.2.3newArchEnabled=true)minifyEnabled true+shrinkResources truein releaseSteps to reproduce
To make it visible in a clean project without minification, we import
GestureHandlerRootViewdirectly by its path instead of from the package root:On Android this crashes every time, also in debug.
I know deep import is not public API and nobody should write this in a real app. We use the normal import in our app. It is only a way to show the same end state in a simple example, because it removes everything else that the package root pulls in.
The import graph shows that
lib/module/index.jshasNativeRNGestureHandlerModule, however in some cases it's somehow skipped or removed, which could cause an issue withGestureHandlerRootView.A link to a Gist, an Expo Snack or a link to a repository based on this template that reproduces the bug.
https://github.com/ArturKalach/rngh3-android-module-id-repro
Gesture Handler version
3.3.0
React Native version
0.87.1
Platforms
Android
JavaScript runtime
Hermes
Workflow
React Native (without Expo)
Architecture
New Architecture (Fabric)
Build type
Release mode
Device
Android emulator
Device model
Android Emulator, Real Device
Acknowledgements
Yes