Skip to content

[Android] com.facebook.react.views.scroll.ReactScrollView.onTouchEvent java.lang.IllegalArgumentException #3921

Closed

Description

@exzos28

Description

Hello, I have a clicker game, and I see a huge number of crashes with an error.

There was pull request #3642, but these changes were not accepted.

Please advise if I can expect this bug to be reviewed.

User-perceived crash rate 4.26%

Image

Almost all crashes are related to this error.

Full log:

Exception java.lang.IllegalArgumentException: pointerIndex out of range
  at android.view.MotionEvent.nativeGetAxisValue
  at android.view.MotionEvent.getY (MotionEvent.java:2447)
  at android.widget.ScrollView.onTouchEvent (ScrollView.java:895)
  at com.facebook.react.views.scroll.ReactScrollView.onTouchEvent (ReactScrollView.java:548)
  at com.swmansion.gesturehandler.core.NativeViewGestureHandler$NativeViewGestureHandlerHook$DefaultImpls.sendTouchEvent (NativeViewGestureHandler.kt:252)
  at com.swmansion.gesturehandler.core.NativeViewGestureHandler$ScrollViewHook.sendTouchEvent (NativeViewGestureHandler.kt:337)
  at com.swmansion.gesturehandler.core.NativeViewGestureHandler.onHandle (NativeViewGestureHandler.kt:149)
  at com.swmansion.gesturehandler.core.GestureHandler.handle (GestureHandler.kt:392)
  at com.swmansion.gesturehandler.core.GestureHandlerOrchestrator.deliverEventToGestureHandler (GestureHandlerOrchestrator.kt:314)
  at com.swmansion.gesturehandler.core.GestureHandlerOrchestrator.deliverEventToGestureHandlers (GestureHandlerOrchestrator.kt:266)
  at com.swmansion.gesturehandler.core.GestureHandlerOrchestrator.onTouchEvent (GestureHandlerOrchestrator.kt:57)
  at com.swmansion.gesturehandler.react.RNGestureHandlerRootHelper.dispatchTouchEvent (RNGestureHandlerRootHelper.kt:119)
  at com.swmansion.gesturehandler.react.RNGestureHandlerRootView.dispatchTouchEvent (RNGestureHandlerRootView.kt:37)
  at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:3131)
  at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2812)
  at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:3131)
  at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2812)
  at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:3131)
  at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2812)
  at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:3131)
  at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2812)
  at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:3131)
  at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2812)
  at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:3131)
  at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2812)
  at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:3131)
  at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2812)
  at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:3131)
  at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:2812)
  at com.android.internal.policy.DecorView.superDispatchTouchEvent (DecorView.java:572)
  at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent (PhoneWindow.java:1895)
  at android.app.Activity.dispatchTouchEvent (Activity.java:4274)
  at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent (WindowCallbackWrapper.java:69)
  at io.sentry.android.core.internal.gestures.WindowCallbackAdapter.dispatchTouchEvent (WindowCallbackAdapter.java:39)
  at io.sentry.android.core.internal.gestures.SentryWindowCallback.dispatchTouchEvent (SentryWindowCallback.java:66)
  at io.sentry.android.replay.util.FixedWindowCallback.dispatchTouchEvent (FixedWindowCallback.java:68)
  at io.sentry.android.replay.gestures.GestureRecorder$SentryReplayGestureRecorder.dispatchTouchEvent (GestureRecorder.kt:84)
  at io.sentry.android.core.internal.gestures.WindowCallbackAdapter.dispatchTouchEvent (WindowCallbackAdapter.java:39)
  at io.sentry.android.core.internal.gestures.SentryWindowCallback.dispatchTouchEvent (SentryWindowCallback.java:66)
  at com.android.internal.policy.DecorView.dispatchTouchEvent (DecorView.java:530)
  at android.view.View.dispatchPointerEvent (View.java:15117)
  at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent (ViewRootImpl.java:6663)
  at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess (ViewRootImpl.java:6458)
  at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:5901)
  at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:5958)
  at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:5924)
  at android.view.ViewRootImpl$AsyncInputStage.forward (ViewRootImpl.java:6089)
  at android.view.ViewRootImpl$InputStage.apply (ViewRootImpl.java:5932)
  at android.view.ViewRootImpl$AsyncInputStage.apply (ViewRootImpl.java:6146)
  at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:5905)
  at android.view.ViewRootImpl$InputStage.onDeliverToNext (ViewRootImpl.java:5958)
  at android.view.ViewRootImpl$InputStage.forward (ViewRootImpl.java:5924)
  at android.view.ViewRootImpl$InputStage.apply (ViewRootImpl.java:5932)
  at android.view.ViewRootImpl$InputStage.deliver (ViewRootImpl.java:5905)
  at android.view.ViewRootImpl.deliverInputEvent (ViewRootImpl.java:8906)
  at android.view.ViewRootImpl.doProcessInputEvents (ViewRootImpl.java:8857)
  at android.view.ViewRootImpl.enqueueInputEvent (ViewRootImpl.java:8825)
  at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent (ViewRootImpl.java:9042)
  at android.view.InputEventReceiver.dispatchInputEvent (InputEventReceiver.java:281)
  at android.os.MessageQueue.nativePollOnce
  at android.os.MessageQueue.next (MessageQueue.java:340)
  at android.os.Looper.loopOnce (Looper.java:168)
  at android.os.Looper.loop (Looper.java:299)
  at android.app.ActivityThread.main (ActivityThread.java:8319)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:556)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1038)

Steps to reproduce

I can't reproduce this on my devices. The usage is approximately as follows:

import {Canvas} from '@shopify/react-native-skia';

import {GestureHandlerRootView, ScrollView, GestureDetector} from 'react-native-gesture-handler';

<ScrollView>
  <GestureHandlerRootView>
     <GestureDetector gesture={...}>
       <Canvas
          pointerEvents="none"
          ....
       />
     </GestureDetector>
  </GestureHandlerRootView>
</ScrollView>

A link to a Gist, an Expo Snack or a link to a repository based on this template that reproduces the bug.

Gesture Handler version

2.30.0

React Native version

0.81.5

Platforms

Android

JavaScript runtime

Hermes

Workflow

None

Architecture

New Architecture (Fabric)

Build type

None

Device

Real device

Device model

No response

Acknowledgements

Yes

Activity

  1. github-actions commented on Jan 15, 2026

    @github-actions

    Hey! 👋

    It looks like you've omitted a few important sections from the issue template.

    Please complete A link to a Gist, an Expo Snack or a link to a repository based on this template that reproduces the bug. section.

  2. github-actions commented on Jan 15, 2026

    @github-actions

    Hey! 👋

    The issue doesn't seem to contain a minimal reproduction.

    Could you provide a snack or a link to a GitHub repository under your username that reproduces the problem?

  3. lxr-mwhite commented on Jan 26, 2026

    @lxr-mwhite

    Seeing the exact same issue on the following versions:

    react-native-gesture-handler: 2.28.0
    react-native: 0.79.6
    

    And on the legacy architecture.

    But most of the crashes I'm seeing occur on Android 16.

    I will try to reproduce locally so I can share more info.

    These are some related error messages I can see in Crashlytics.

    invalid pointerIndex -1 for MotionEvent { action=POINTER_UP(1), id[0]=0, x[0]=547.05, y[0]=336.1, id[1]=1, x[1]=899.1, y[1]=427.375, flags=SUPPORTS_ORIENTATION, pointerCount=2, eventTime=226307463231000ns, downTime=226302971810000ns, deviceId=3, source=TOUCHSCREEN, displayId=0, eventId=0x3b28b3a0}
    
    invalid pointerIndex -1 for MotionEvent { action=POINTER_UP(2), id[0]=0, x[0]=429.509, y[0]=-264.833, id[1]=1, x[1]=495.562, y[1]=20.7217, id[2]=2, x[2]=392.786, y[2]=368.788, id[3]=3, x[3]=66.375, y[3]=1065.8, flags=SUPPORTS_ORIENTATION, pointerCount=4, eventTime=927759325804000ns, downTime=927758311044000ns, deviceId=3, source=TOUCHSCREEN, displayId=0, eventId=0x3cc12d99}
    
  4. j-piasecki commented on Jan 27, 2026

    @j-piasecki
    Member

    I wasn't able to reproduce the issue either, but I might have an idea of what's happening. Would it be possible for you to test this PR: #3933?

  5. exzos28 commented on Jan 27, 2026

    @exzos28
    Author

    @j-piasecki I don't think I'll be able to conduct a new experiment in production.

    At the moment, I've patched in the changes from 3642, and it looks like that has solved all the crashes with the onTouchEvent method.

  6. lxr-mwhite commented on Mar 31, 2026

    @lxr-mwhite

    @exzos28 Are you still having success with the patch from #3642?
    @j-piasecki Thank you for your fix in #3933.

    I haven't been able to try either patch yet, but have still seen both issues on our latest app version so I'm wondering if they are related. They occur about 5-10 times per day and more often on Google Pixels.

    On Android 10 I see the exception: Fatal Exception: java.lang.IllegalArgumentException: pointerIndex out of range while on newer versions (mostly Android 16) I see the exception: Fatal Exception: java.lang.IllegalArgumentException: invalid pointerIndex -1 for MotionEvent { action=POINTER_UP(1), id[0]=0, x[0]=757.5, y[0]=114.25, id[1]=3, x[1]=992.25, y[1]=70, flags=SUPPORTS_ORIENTATION, pointerCount=2, eventTime=314555878149000ns, downTime=314555290686000ns, deviceId=4, source=TOUCHSCREEN, displayId=0, eventId=0xd408b67e} .

    I can possibly try one of these patches in an upcoming release, I'm just unsure if both PRs end up fixing the same thing or if there are two different issues.

  7. j-piasecki commented on Apr 1, 2026

    @j-piasecki
    Member

    @lxr-mwhite Those exceptions look like they share the same root cause, so both patches are targeting them. #3642 is simply catching the thrown exception and effectively ignoring it, while #3933 is attempting to address the root cause.

    The first one will definitely clear up the issues you're seeing, but I'd appreciate it if you could try the latter (which may not help with the problem).

  8. lxr-mwhite commented on Apr 1, 2026

    @lxr-mwhite

    @j-piasecki Thank you for the clarification.
    I will include a patch with the changes from #3933 in our next release towards the end of the month and report back shortly after.

  9. lxr-mwhite commented on Apr 24, 2026

    @lxr-mwhite

    @j-piasecki Just providing an update that the latest release of our app including a patch with the changes from your PR #3933 is still experiencing some crashes with the same error that I reported earlier.

    So far they have all occurred on Pixel devices running Android 16. (Pixel 8, Pixel 9, Pixel Tablet).

    I'm not sure if this is still considered the same error as the one originally reported in this issue. Please let me know if you would prefer a separate issue for this.

    Fatal Exception: java.lang.IllegalArgumentException: invalid pointerIndex -1 for MotionEvent { action=POINTER_UP(0), id[0]=0, x[0]=372, y[0]=907, id[1]=1, x[1]=670, y[1]=313, flags=SUPPORTS_ORIENTATION | SUPPORTS_DIRECTIONAL_ORIENTATION, pointerCount=2, eventTime=1393156794000000ns, downTime=1393156259000000ns, deviceId=4, source=TOUCHSCREEN, displayId=0, eventId=0xdc0b4416}
           at android.view.MotionEvent.nativeGetAxisValue(MotionEvent.java)
           at android.view.MotionEvent.getY(MotionEvent.java:2922)
           at android.widget.ScrollView.onTouchEvent(ScrollView.java:987)
           at com.facebook.react.views.scroll.ReactScrollView.onTouchEvent(ReactScrollView.java:548)
           at com.swmansion.gesturehandler.core.NativeViewGestureHandler$NativeViewGestureHandlerHook$DefaultImpls.sendTouchEvent(NativeViewGestureHandler.kt:257)
           at com.swmansion.gesturehandler.core.NativeViewGestureHandler$ScrollViewHook.sendTouchEvent(NativeViewGestureHandler.kt)
           at com.swmansion.gesturehandler.core.NativeViewGestureHandler.onHandle(NativeViewGestureHandler.kt:154)
           at com.swmansion.gesturehandler.core.GestureHandler.handle(GestureHandler.kt:403)
           at com.swmansion.gesturehandler.core.GestureHandlerOrchestrator.deliverEventToGestureHandler(GestureHandlerOrchestrator.kt:314)
           at com.swmansion.gesturehandler.core.GestureHandlerOrchestrator.deliverEventToGestureHandlers(GestureHandlerOrchestrator.kt:266)
           at com.swmansion.gesturehandler.core.GestureHandlerOrchestrator.onTouchEvent(GestureHandlerOrchestrator.kt:57)
           at com.swmansion.gesturehandler.react.RNGestureHandlerRootHelper.dispatchTouchEvent(RNGestureHandlerRootHelper.kt:119)
           at com.swmansion.gesturehandler.react.RNGestureHandlerRootView.dispatchTouchEvent(RNGestureHandlerRootView.kt:37)
           at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3143)
           at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2826)
           at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3143)
           at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2826)
           at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3143)
           at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2826)
           at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3143)
           at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2826)
           at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3143)
           at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2826)
           at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3143)
           at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2826)
           at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3143)
           at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2826)
           at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:503)
           at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:2017)
           at android.app.Activity.dispatchTouchEvent(Activity.java:4666)
           at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
           at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:441)
           at android.view.View.dispatchPointerEvent(View.java:17196)
           at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:8585)
           at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:8335)
           at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7702)
           at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7759)
           at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7725)
           at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:7896)
           at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7733)
           at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:7953)
           at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7706)
           at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7759)
           at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7725)
           at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7733)
           at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7706)
           at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:11033)
           at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:10973)
           at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:10941)
           at android.view.ViewRootImpl.processRawInputEvent(ViewRootImpl.java:11377)
           at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:11161)
           at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:284)
           at android.os.MessageQueue.nativePollOnce(MessageQueue.java)
           at android.os.MessageQueue.nextDeliQueue(MessageQueue.java:780)
           at android.os.MessageQueue.next(MessageQueue.java:760)
           at android.os.Looper.loopOnce(Looper.java:196)
           at android.os.Looper.loop(Looper.java:367)
           at android.app.ActivityThread.main(ActivityThread.java:9333)
           at java.lang.reflect.Method.invoke(Method.java)
           at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:566)
           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:929)
    
  10. j-piasecki commented on Apr 27, 2026

    @j-piasecki
    Member

    @lxr-mwhite Do you have enough data to say whether the crash rate was influenced in any way by introducing this patch?

    Also, earlier, you mentioned seeing Fatal Exception: java.lang.IllegalArgumentException: pointerIndex out of range on older Android versions. Do you still see it, or did that stop occurring after the patch?

  11. lxr-mwhite commented on Apr 27, 2026

    @lxr-mwhite

    @j-piasecki Good questions and sorry for not providing that information earlier. I was hoping to give it a bit more time to see.

    It has now been 5 days since our release, and I would say the crash rate was unfortunately not influenced. I have now also seen the Fatal Exception: java.lang.IllegalArgumentException: pointerIndex out of range on older Android versions with the patch.

    Some good news is that our app releases will be more frequent now so we should be able to get feedback more quickly. We will be adding some additional logs temporarily to see if we can determine a pattern to help reproduce it locally.

  12. lxr-mwhite commented on May 10, 2026

    @lxr-mwhite

    @j-piasecki I've been continuing to investigate this crash and try to get reliable reproduction steps but unfortunately have not been able to come up with anything concrete. Though I've also been trying to better understand the GestureHandler code and wanted to share a potential solution.

    Since the crash originates from ScrollViewHook.sendTouchEvent, which inherits the default implementation and forwards all events to the native ScrollView via view?.onTouchEvent(event), I wonder if we could validate the action and drop the malformed event?

    I was thinking that ACTION_POINTER_UP with a pointerCount greater than 1 means one finger is lifting but one or more are still down so the gesture isn't over yet. onHandle() handles ACTION_UP in its own branch independently of STATE_ACTIVE, so the gesture should still transition to END cleanly when the last finger lifts. Dropping only the malformed POINTER_UP event should hopefully not interrupt or freeze the scroll interaction.

    As a potential solution, can ScrollViewHook override sendTouchEvent to validate the action index:

    private class ScrollViewHook : NativeViewGestureHandlerHook {
      override fun shouldCancelRootViewGestureHandlerIfNecessary() = true
    
      override fun sendTouchEvent(view: View?, event: MotionEvent): Boolean? {
        if (event.actionMasked == MotionEvent.ACTION_POINTER_UP) {
          val actionIndex = event.actionIndex;
          if (actionIndex < 0 || actionIndex >= event.pointerCount) {
            return null
          }
        }      
        return view?.onTouchEvent(event)
      }
    }

    I was thinking it might be acceptable to introduce this defensive pattern, similar to what @m-bert introduced for ACTION_MOVE events in #4010 (if I understood the changes correctly).

    While I was not able to reproduce the crash locally through a normal interaction, I was able to confirm the code path by constructing a MotionEvent with a deliberately invalid actionIndex. This produced a stack trace with the same IllegalArgumentException: invalid pointerIndex for MotionEvent.

  13. j-piasecki commented on May 12, 2026

    @j-piasecki
    Member

    I was thinking that ACTION_POINTER_UP with a pointerCount greater than 1 means one finger is lifting but one or more are still down so the gesture isn't over yet. onHandle() handles ACTION_UP in its own branch independently of STATE_ACTIVE, so the gesture should still transition to END cleanly when the last finger lifts. Dropping only the malformed POINTER_UP event should hopefully not interrupt or freeze the scroll interaction.

    That would very likely work as you expect, but it addresses the symptom like #3642 instead of the root cause, which is still not found. It's possible that it's still the best approach, given that the ScrollView is the only report of that issue we've been getting, AFAIK.

  14. lxr-mwhite commented on May 12, 2026

    @lxr-mwhite

    @j-piasecki I appreciate all the help on this!
    I've looked at some additional logs and tried to pay close attention to the actual coordinates of the pointers causing the crash, and have found some of them pretty fascinating. For every invalid pointerIndex -1 for MotionEvent crash, I noticed it was the POINTER_UP action with a pointerCount > 1. I've noticed in some instances that the y coordinate is negative, which I don't understand. I've seen it crash in areas of my app where we don't even have anything on the screen that would suggest a multi touch gesture is available. And finally, I've noticed some users experience crashes within the first few seconds while others do after a few minutes.

    Since it doesn't seem like anyone else is facing this issue, I have to start thinking it might be related to something specific in mine. So after collecting a few logs and crash reports I've looked at some of the code in the areas where the crashes occur and found a few things that I will change for our next release.

    1. Some of the screens are very basic, like a "Forgot Password" screen. But the container view is a ScrollView imported from react-native-gesture-handler. Found a couple screens where that is the case, so I will replace those imports with the ones from react-native.
    2. Still using the deprecated NativeViewGestureHandler. One instance wraps a FlatList and a second instance wraps a SectionList. I will use FlatList from react-native-gesture-handler instead, and for the second component I'll migrate to GestureDetector.
    3. I noticed the following warning log: [GESTURE HANDLER] Gesture handler is already enabled for a parent view. I found that another one of our dependencies is wrapping its exported component with GestureHandlerRootView. We already wrap our NavigationContainer with GestureHandlerRootView, so I will patch the other package to remove its GestureHandlerRootView.
    4. I noticed another warning log: Can't cancel already finished gesture. Is a child View trying to start a gesture from an UP/CANCEL event?. This is logged when our FlatList Item's Gesture.Tap().onEnd() is fired. I don't have a plan for this one yet.

    Thought I would note these down in case anyone else comes across a similar issue in the future.

  15. j-piasecki commented on May 12, 2026

    @j-piasecki
    Member

    Some of the screens are very basic, like a "Forgot Password" screen. But the container view is a ScrollView imported from react-native-gesture-handler. Found a couple screens where that is the case, so I will replace those imports with the ones from react-native.

    Assuming the bug is in RNGH, this will likely get rid of the issue, since the ScrollView's touch events will not go through the Gesture Handler touch system.

    Have you tried to check if the snippet you posted above (customized ScrollViewHook) solves the issue in your case? I feel like this is a rare case where a literal if (goingToCrash) { dont() } may be applicable 😄.

  16. lxr-mwhite commented on May 12, 2026

    @lxr-mwhite

    Have you tried to check if the snippet you posted above (customized ScrollViewHook) solves the issue in your case? I feel like this is a rare case where a literal if (goingToCrash) { dont() } may be applicable 😄.

    I will try the patch in my next release as well. I was hesitant at first because it would be harder to tell if the JS changes fixed the issues on their own or not. But one of the most used screens in my app is using an RNGH ScrollView, and that one I don't think I can replace. So the patch may help with the crashes occurring on that screen.

    I can report back after trying the ScrollViewHook patch.

  17. lxr-mwhite commented on May 13, 2026

    @lxr-mwhite

    @j-piasecki I was finally able to reproduce this locally. Mostly by chance at first, but after further investigation I can now reproduce it fairly reliably.

    Unfortunately the ScrollViewHook changes didn’t help. I also still have your changes from #3933 applied, but tested both with and without them.

    I’m reproducing this on an Android 16 emulator. The scenario is:

    • An RNGH FlatList on my Main screen
    • Tapping a card navigates to a Details screen
    • Very quickly after selecting an item, I perform a multi-touch gesture on the FlatList while the parent screen is navigating away

    I also added a small artificial delay during debugging to make the transition timing easier to hit consistently.

    It still doesn’t happen 100% of the time, which may explain why it has been difficult to reproduce locally and why only a relatively small number of users have experienced it in production.

    At this point I’m less convinced the root issue is purely in RNGH itself, and more that ScrollView is receiving an event sequence that leaves its internal active pointer state inconsistent during the navigation transition.

    As a temporary mitigation, I may patch ScrollViewHook like this:

    override fun sendTouchEvent(view: View?, event: MotionEvent): Boolean? {
      return try {
        view?.onTouchEvent(event)
      } catch (e: IllegalArgumentException) {
        Log.w(ReactConstants.TAG, "[GESTURE HANDLER] Ignoring invalid ScrollView MotionEvent", e)
        null
      }
    }

    Let me know if there’s any additional logging or instrumentation I can add that would help narrow this down further.

  18. j-piasecki commented on May 14, 2026

    @j-piasecki
    Member

    Thank you for the repro steps, I managed to build a small reproducer based on them!

    Here it is
    import { NavigationContainer } from '@react-navigation/native';
    import type { NativeStackScreenProps } from '@react-navigation/native-stack';
    import { createNativeStackNavigator } from '@react-navigation/native-stack';
    import { StyleSheet, Text, View } from 'react-native';
    import {
      FlatList,
      GestureHandlerRootView,
      Touchable,
    } from 'react-native-gesture-handler';
    
    type RootStackParamList = {
      Main: undefined;
      Details: { id: number };
    };
    
    const Stack = createNativeStackNavigator<RootStackParamList>();
    
    const ITEMS = Array.from({ length: 50 }, (_, i) => ({
      id: i,
      title: `Card ${i + 1}`,
    }));
    
    const NAVIGATE_DELAY_MS = 500;
    
    export default function App() {
      return (
        <GestureHandlerRootView style={styles.root}>
          <NavigationContainer>
            <Stack.Navigator
              screenOptions={{
                animation: 'slide_from_right',
              }}>
              <Stack.Screen
                name="Main"
                component={MainScreen}
                options={{ title: 'Main' }}
              />
              <Stack.Screen
                name="Details"
                component={DetailsScreen}
                options={{ title: 'Details' }}
              />
            </Stack.Navigator>
          </NavigationContainer>
        </GestureHandlerRootView>
      );
    }
    
    function MainScreen({
      navigation,
    }: NativeStackScreenProps<RootStackParamList, 'Main'>) {
      return (
        <View style={styles.container}>
          <FlatList
            data={ITEMS}
            keyExtractor={(item) => item.id.toString()}
            renderItem={({ item }) => (
              <Touchable
                style={styles.card}
                onPress={() => {
                  setTimeout(() => {
                    navigation.navigate('Details', { id: item.id });
                  }, NAVIGATE_DELAY_MS);
                }}>
                <Text style={styles.cardText}>{item.title}</Text>
              </Touchable>
            )}
          />
        </View>
      );
    }
    
    function DetailsScreen({
      route,
    }: NativeStackScreenProps<RootStackParamList, 'Details'>) {
      return (
        <View style={styles.detailsContainer}>
          <Text style={styles.detailsText}>
            Details for item #{route.params.id}
          </Text>
        </View>
      );
    }
    
    const styles = StyleSheet.create({
      root: {
        flex: 1,
      },
      container: {
        flex: 1,
        backgroundColor: '#fff',
      },
      card: {
        paddingVertical: 20,
        paddingHorizontal: 16,
        borderBottomWidth: StyleSheet.hairlineWidth,
        borderBottomColor: '#ccc',
        backgroundColor: '#fff',
      },
      cardText: {
        fontSize: 16,
        color: '#000',
      },
      detailsContainer: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: '#fff',
      },
      detailsText: {
        fontSize: 18,
        color: '#000',
      },
    });

    You have to tap on any item, start a multi-touch gesture before the navigation starts, and finish it during the navigation animation. I'll look into this more soon.

  19. lxr-mwhite commented on May 14, 2026

    @lxr-mwhite

    Thank you for all the help @j-piasecki !

  20. j-piasecki commented on May 14, 2026

    @j-piasecki
    Member

    @lxr-mwhite Can I ask you to check if #4177 works in your case? It solved the issue in the small reproducer.

  21. lxr-mwhite commented on May 14, 2026

    @lxr-mwhite

    @lxr-mwhite Can I ask you to check if #4177 works in your case? It solved the issue in the small reproducer.

    So far so good! I can confirm the same steps I was reproducing the crash with yesterday no longer produce the crash with the changes from #4177. I even added a breakpoint to verify it goes though the parent.indexOfChild(current) < 0 -> return false code path and confirmed it does.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions