Skip to content

Commit d672c96

Browse files
tomekzawmeta-codesync[bot]
authored andcommitted
Fix IllegalArgumentException crash in ScrollView.onTouchEvent on Android (#56926)
Summary: Android's framework `ScrollView` can throw `IllegalArgumentException: pointerIndex out of range` from `MotionEvent.getY()` in `ScrollView.onTouchEvent` when the view's tracked active pointer becomes stale after a multi-touch `ACTION_POINTER_UP`. ### This is a known Android framework bug The same exception in sibling widgets (`SwipeRefreshLayout`, `ViewPager`) has been reported and worked around with `try`/`catch` since at least 2014 — see the [Stack Overflow question](https://stackoverflow.com/questions/27662682/illegalargumentexception-pointerindex-out-of-range-from-swiperefreshlayout) already linked in the existing in-tree comment, and Google's AOSP [Issue #36987494](https://issuetracker.google.com/issues/36987494). React Native added the same workaround to `ReactScrollView.onInterceptTouchEvent` in [67c3ad4](67c3ad4) (2018-02-16, originally proposed in #12085 and #13166), with the comment: ```java } catch (IllegalArgumentException e) { // Log and ignore the error. This seems to be a bug in the android SDK and // this is the commonly accepted workaround. // https://tinyurl.com/mw6qkod (Stack Overflow) FLog.w(ReactConstants.TAG, "Error intercepting touch event.", e); } ``` The bug is still active on current Android releases — the production stack trace below is from **Android 15 (SDK 35) on a Motorola moto g15, May 2026**. ### The gap this PR closes `ReactScrollView`, `ReactHorizontalScrollView` and `ReactNestedScrollView` already catch this `IllegalArgumentException` in `onInterceptTouchEvent`, but **the same catch is missing from `onTouchEvent`** in all three classes, even though `super.onTouchEvent(ev)` reaches the same framework code path that can throw. This PR mirrors the existing in-file workaround into `onTouchEvent`. ### Production stack trace (Sentry, May 2026) **Device:** Motorola moto g15 **OS:** Android 15 (SDK 35, build `VVTA35.51-153`) **App stack:** React Native 0.85.3 with react-native-gesture-handler ``` java.lang.IllegalArgumentException: invalid pointerIndex -1 for MotionEvent { action=POINTER_UP(1), id[0]=0, x[0]=591.75, y[0]=153.185, id[1]=1, x[1]=875.5, y[1]=194.935, pointerCount=2, eventTime=15421926904000, downTime=15421677988000, deviceId=12, source=TOUCHSCREEN, displayId=0, eventId=0x38b97d3} at android.view.MotionEvent.nativeGetAxisValue(MotionEvent.java) at android.view.MotionEvent.getY(MotionEvent.java:2828) at android.widget.ScrollView.onTouchEvent(ScrollView.java:941) at com.facebook.react.views.scroll.l.onTouchEvent(r8-map-id-42abedea19c8d6be0f67a7054855378c0d176430e10d921ab2155e03012a3488:77) at android.view.View.performOnTouchCallback(View.java:16474) at android.view.View.dispatchTouchEvent(View.java:16426) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3169) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2811) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3197) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at com.swmansion.gesturehandler.react.k.dispatchTouchEvent(r8-map-id-42abedea19c8d6be0f67a7054855378c0d176430e10d921ab2155e03012a3488:47) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3175) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2825) at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:458) at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1980) at android.app.Activity.dispatchTouchEvent(Activity.java:4579) at androidx.appcompat.view.i.dispatchTouchEvent(r8-map-id-42abedea19c8d6be0f67a7054855378c0d176430e10d921ab2155e03012a3488:3) at io.sentry.android.core.internal.gestures.k.dispatchTouchEvent(r8-map-id-42abedea19c8d6be0f67a7054855378c0d176430e10d921ab2155e03012a3488:3) at io.sentry.android.core.internal.gestures.i.dispatchTouchEvent(r8-map-id-42abedea19c8d6be0f67a7054855378c0d176430e10d921ab2155e03012a3488:40) at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:416) at android.view.View.dispatchPointerEvent(View.java:16761) at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:8145) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:7889) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7281) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7338) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7304) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:7470) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7312) at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:7527) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7285) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7338) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7304) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7312) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7285) at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:10505) at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:10452) at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:10407) at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:10670) at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:295) at android.os.MessageQueue.nativePollOnce(MessageQueue.java) at android.os.MessageQueue.next(MessageQueue.java:346) at android.os.Looper.loopOnce(Looper.java:191) at android.os.Looper.loop(Looper.java:319) at android.app.ActivityThread.main(ActivityThread.java:8754) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:962) ``` The `MotionEvent` dump pinpoints the trigger: a two-finger `ACTION_POINTER_UP(1)` where the framework `ScrollView`'s tracked `mActivePointerId` is stale, so `findPointerIndex(...)` returns `-1` and `getY(-1)` throws from native code. Closes #30320 Closes #29642 Both issues were auto-closed by the stale bot without a fix shipping, despite multiple confirmed reproductions over 4+ years. ## Changelog: [ANDROID] [FIXED] - Catch IllegalArgumentException in ScrollView.onTouchEvent to prevent crashes from a known Android framework multi-touch bug Pull Request resolved: #56926 Test Plan: Can't really reproduce locally — the crash happens very infrequently in production. The Sentry stack trace above is what triggered this PR. Reviewed By: fabriziocucci Differential Revision: D106042303 Pulled By: Abbondanzo fbshipit-source-id: fe662a390673418a558da83c7bd94011bbf0cab5
1 parent 58012fb commit d672c96

3 files changed

Lines changed: 28 additions & 4 deletions

File tree

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,15 @@ public boolean onTouchEvent(MotionEvent ev) {
838838
cancelPostTouchScrolling();
839839
}
840840

841-
return super.onTouchEvent(ev);
841+
try {
842+
return super.onTouchEvent(ev);
843+
} catch (IllegalArgumentException e) {
844+
// Log and ignore the error. This seems to be a bug in the android SDK and
845+
// this is the commonly accepted workaround.
846+
// https://tinyurl.com/mw6qkod (Stack Overflow)
847+
FLog.w(ReactConstants.TAG, "Error handling touch event.", e);
848+
return false;
849+
}
842850
}
843851

844852
@Override

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactNestedScrollView.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<2aa191314924bd0f969fba0e64b86142>>
7+
* @generated SignedSource<<ee631e2aaec71e0722c894a07f4f7022>>
88
*/
99

1010
/**
@@ -680,7 +680,15 @@ public boolean onTouchEvent(MotionEvent ev) {
680680
cancelPostTouchScrolling();
681681
}
682682

683-
return super.onTouchEvent(ev);
683+
try {
684+
return super.onTouchEvent(ev);
685+
} catch (IllegalArgumentException e) {
686+
// Log and ignore the error. This seems to be a bug in the android SDK and
687+
// this is the commonly accepted workaround.
688+
// https://tinyurl.com/mw6qkod (Stack Overflow)
689+
FLog.w(ReactConstants.TAG, "Error handling touch event.", e);
690+
return false;
691+
}
684692
}
685693

686694
@Override

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,15 @@ public boolean onTouchEvent(MotionEvent ev) {
672672
cancelPostTouchScrolling();
673673
}
674674

675-
return super.onTouchEvent(ev);
675+
try {
676+
return super.onTouchEvent(ev);
677+
} catch (IllegalArgumentException e) {
678+
// Log and ignore the error. This seems to be a bug in the android SDK and
679+
// this is the commonly accepted workaround.
680+
// https://tinyurl.com/mw6qkod (Stack Overflow)
681+
FLog.w(ReactConstants.TAG, "Error handling touch event.", e);
682+
return false;
683+
}
676684
}
677685

678686
@Override

0 commit comments

Comments
 (0)