Repository navigation
Description
Description
What happens: on Android the app crashes with a NullPointerException in GestureHandler.moveToState (orchestrator!!) when a gesture handler is detached at the same time as its gesture finishes.
Expected: detaching a handler whose gesture has just ended is a no-op (or a clean cancel), never a crash.
FATAL EXCEPTION: main
java.lang.NullPointerException
at com.swmansion.gesturehandler.core.GestureHandler.moveToState(GestureHandler.kt:633)
at com.swmansion.gesturehandler.core.GestureHandler.cancel(GestureHandler.kt:741)
at com.swmansion.gesturehandler.react.RNGestureHandlerRegistry.detachHandlerInternal$lambda$11(RNGestureHandlerRegistry.kt:138)
at com.swmansion.gesturehandler.react.RNGestureHandlerRegistry$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7935)
Why (from the 3.3.0 sources): it is a race between RNGestureHandlerRegistry.detachHandlerInternal and the end of the gesture.
detachHandlerInternalcheckshandler.view != nullsynchronously (the handler is still prepared, gesture in progress) and postshandler.cancel()withUiThreadUtil.runOnUiThread.- Before the posted runnable runs, the finger is lifted: the gesture ends and the orchestrator calls
reset()on the handler, which setsview = nullandorchestrator = null; the state goes back toSTATE_UNDETERMINED. - The posted
cancel()now runs on a reset handler.cancel()acceptsSTATE_UNDETERMINED, callsmoveToState(STATE_CANCELLED), andorchestrator!!.onHandlerStateChange(...)throws.
So the view != null check and the cancel() are not atomic. The same code is in 2.28.0, 2.33.0, 3.3.0 and 3.4.0-nightly-20261002.
We hit it with react-native-sortables (drag & drop grid): on drop the grid re-renders and re-mounts the dragged item, so the detach of its handlers lands right when the finger is lifted. It happens every few drags on a real device.
Workaround we ship (patch-package), which removed the crash on the same device:
- orchestrator!!.onHandlerStateChange(this, newState, oldState)
+ orchestrator?.onHandlerStateChange(this, newState, oldState)A more targeted fix could be re-checking the handler inside the posted runnable (e.g. if (handler.view != null) handler.cancel()), or making cancel() a no-op on a handler that has been reset.
Steps to reproduce
- Render a
Sortable.Gridfromreact-native-sortables1.10.1 withcustomHandle, whose items contain components usingGestureDetector(Gesture.Tap/Gesture.LongPresscomposed withGesture.Exclusive). - On Android, long press a
Sortable.Handleand drag an item to a new position, then lift the finger. - In
onDragEnd, update the data so that the grid re-renders and the dropped item is re-mounted (itsGestureDetectors are detached). - Repeat steps 2-3: after a few drops the app crashes with the stack trace above.
It is a timing race, so it is intermittent: it reproduced on a real device (Sunmi D3 MINI, Android 13) every few drags and not on an Android emulator.
A link to a Gist, an Expo Snack or a link to a repository based on this template that reproduces the bug.
No standalone repository yet: it is a race and we could not make it deterministic outside the device. The analysis above points to the exact lines (RNGestureHandlerRegistry.kt:138 posting cancel(), GestureHandler.reset() clearing orchestrator, GestureHandler.kt:633 dereferencing it).
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
Real device
Device model
Sunmi D3 MINI (Android 13)
Related libraries
- react-native-reanimated 4.7.1
- react-native-worklets 0.13.0
- react-native-screens 4.28.0
- react-native-sortables 1.10.1
Acknowledgements
Yes
Activity
github-actions commentedon Oct 5, 2026
Hey @Davide-Michelotti! 👋
Some of the required information in this issue is missing, incomplete or invalid. Could you take another look at the template and make sure each of these is filled in with something we can act on?
package.jsonor lockfilepackage.jsonor lockfileOnce the issue is updated we will take another look. Thanks!
github-actions commentedon Oct 5, 2026
Hey @Davide-Michelotti! 👋
The issue doesn't seem to contain a minimal reproduction.
Could you provide a snack, a gist or a link to a GitHub repository that reproduces the problem?
m-bert commentedon Oct 9, 2026
Hi @Davide-Michelotti! Could you please check whether #4584 fixes this issue?
Davide-Michelotti commentedon Oct 12, 2026
Hi @m-bert, thanks for looking into this!
I tested #4584 on the same device (Sunmi D3 MINI, Android 13, release build, New Architecture), now on React Native 0.88.0-rc.4 with Gesture Handler 3.3.0, Reanimated/Sortables unchanged.
NullPointerExceptionatGestureHandler.moveToState(GestureHandler.kt:633)←cancel←RNGestureHandlerRegistry.detachHandlerInternal), dragging tiles by hand in ourSortable.Grid.Can't cancel already finished gesture…warnings in that session, so the cancel path was hit repeatedly and handled.So #4584 fixes the issue for us. Your explanation also matches our setup better than my original analysis:
react-native-sortablescallscontrol.fail()insideonTouchesCancelled.One note: I could not reproduce it with scripted single-pointer drags (
adb shell input motionevent, ~160 drags on the unpatched build), only by hand.Feel free to close this once the fix is released. Thanks again!
m-bert commentedon Oct 13, 2026
Thank you for confirmation! In that case I'm closing this issue, let me know if you need any further assistance 😅