software-mansion / software-mansion/react-native-gesture-handler
[Android][v3] GestureHandlerRootView reads globalThis._RNGH_MODULE_ID without ensuring the native module is initialized
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 6.8k
- Forks
- 1.1k
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 50
Description
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.0react-native: 0.87.1react: 19.2.3- Platform: Android
- Architecture: new arch (
newArchEnabled=true) - JS engine: Hermes
- Fails with
minifyEnabled true+shrinkResources truein 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
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with src/components/GestureHandlerRootView.android.tsx and src/specs/NativeRNGestureHandlerModule.ts, then trace initialization through shared/runtime/RNGHRuntimeDecorator.cpp and android/src/main/jni/RNGestureHandlerModule.cpp. Use the linked reproducer and inspect android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRootHelper.kt. Done means the reproduced Android crash no longer occurs when the root view renders before native module initialization.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, cpp, kotlin, react-native, typescript
- Domain
- mobile-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100