react-native-reanimated: Runtime crash: JSON object value was a NaN when serializing value at "focalX"

Description

The following crash occurs sporadically in production build.

java.lang.RuntimeException: folly::toJson: JSON object value was a NaN when serializing value at "focalX"
    at com.facebook.react.bridge.NativeMap.toString(NativeMap.java)
    at com.swmansion.reanimated.NativeProxy$EventHandler.receiveEvent(NativeProxy.java)
    at com.swmansion.reanimated.NativeProxy$EventHandler.receiveEvent(NativeProxy.java:55)
    at com.swmansion.gesturehandler.react.RNGestureHandlerStateChangeEvent.dispatch(RNGestureHandlerStateChangeEvent.java:80)
    at com.swmansion.reanimated.NodesManager.handleEvent(NodesManager.java:485)
    at com.swmansion.reanimated.NodesManager.onEventDispatch(NodesManager.java:462)
    at com.facebook.react.uimanager.events.EventDispatcherImpl.dispatchEvent(EventDispatcherImpl.java:116)
    at com.swmansion.gesturehandler.react.RNGestureHandlerModule.onStateChange(RNGestureHandlerModule.java:691)
    at com.swmansion.gesturehandler.react.RNGestureHandlerModule.access$300(RNGestureHandlerModule.java:42)
    at com.swmansion.gesturehandler.react.RNGestureHandlerModule$1.onStateChange(RNGestureHandlerModule.java:436)
    at com.swmansion.gesturehandler.GestureHandler.dispatchStateChange(GestureHandler.java:81)
    at com.swmansion.gesturehandler.GestureHandlerOrchestrator.onHandlerStateChange(GestureHandlerOrchestrator.java:200)
    at com.swmansion.gesturehandler.GestureHandler.moveToState(GestureHandler.java:350)
    at com.swmansion.gesturehandler.GestureHandler.cancel(GestureHandler.java:444)
    at com.swmansion.gesturehandler.GestureHandlerOrchestrator.makeActive(GestureHandlerOrchestrator.java:223)
    at com.swmansion.gesturehandler.GestureHandlerOrchestrator.tryActivate(GestureHandlerOrchestrator.java:159)
    at com.swmansion.gesturehandler.GestureHandlerOrchestrator.onHandlerStateChange(GestureHandlerOrchestrator.java:194)
    at com.swmansion.gesturehandler.GestureHandler.moveToState(GestureHandler.java:350)
    at com.swmansion.gesturehandler.GestureHandler.activate(GestureHandler.java:456)
    at com.swmansion.gesturehandler.NativeViewGestureHandler.onHandle(NativeViewGestureHandler.java:87)
    at com.swmansion.gesturehandler.GestureHandler.handle(GestureHandler.java:329)
    at com.swmansion.gesturehandler.GestureHandlerOrchestrator.deliverEventToGestureHandler(GestureHandlerOrchestrator.java:301)
    at com.swmansion.gesturehandler.GestureHandlerOrchestrator.deliverEventToGestureHandlers(GestureHandlerOrchestrator.java:259)
    at com.swmansion.gesturehandler.GestureHandlerOrchestrator.onTouchEvent(GestureHandlerOrchestrator.java:101)
    at com.swmansion.gesturehandler.react.RNGestureHandlerRootHelper.dispatchTouchEvent(RNGestureHandlerRootHelper.java:126)
    at com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView.dispatchTouchEvent(RNGestureHandlerEnabledRootView.java:36)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3920)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3535)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3920)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3535)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3920)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3535)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3920)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3535)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3920)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3535)
    at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:913)
    at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1957)
    at android.app.Activity.dispatchTouchEvent(Activity.java:4182)
    at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
    at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:871)
    at android.view.View.dispatchPointerEvent(View.java:15458)
    at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:7457)
    at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:7233)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6595)
    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:6652)
    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:6618)
    at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:6786)
    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:6626)
    at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:6843)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6599)
    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:6652)
    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:6618)
    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:6626)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6599)
    at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:9880)
    at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:9718)
    at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:9671)
    at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:10014)
    at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:220)
    at android.os.MessageQueue.nativePollOnce(MessageQueue.java)
    at android.os.MessageQueue.next(MessageQueue.java:335)
    at android.os.Looper.loop(Looper.java:206)
    at android.app.ActivityThread.main(ActivityThread.java:8633)
    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:1130)

Expected behavior

It should not crash.

Actual behavior & steps to reproduce

  • upgrade react-native-reanimated: from 1.13.2 to 2.2.4 in project
  • update

Modify references in the code from (reanimated v1)

  const translateX = Animated.interpolate(progress, {
    inputRange: [0, 0.5, 0.7, 0.8, 1],
    outputRange: [-100, -85, -70, -45, 0],
  });

to (reanimated v2)

  const translateX = Animated.interpolateNode(
    progress as Animated.Adaptable<number>,
    {
      inputRange: [0, 0.5, 0.7, 0.8, 1],
      outputRange: [-100, -85, -70, -45, 0],
    },
  );

and from

React.useEffect(() => {
    Animated.timing(fadeAnim, {
      toValue: 1,
      duration: 1000,
      easing: Easing.inOut(Easing.ease),
    }).start();
  }, [fadeAnim]);

to

React.useEffect(() => {
    Animated.timing(fadeAnim, {
      toValue: 1,
      duration: 1000,
      easing: EasingNode.inOut(EasingNode.ease),
    }).start();
  }, [fadeAnim]);

Those are the only 2 references of react-native-reanimated in my app

Package versions

  • React Native: 0.66.3
  • React Native Reanimated: 2.2.4
  • NodeJS: v16.11.1
  • Xcode: 13.1
  • Java & Gradle: java 15.0.2 2021-01-19 and gradle 6.9

Affected platforms

  • Android
  • iOS
  • Web

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 15 (9 by maintainers)

Most upvoted comments

@vtoupet We release a new version today night or tomorrow morning 😀 (according to middle Europe time)

fair point about other libraries. The solution in the pr looks good, way better than what I did by replacing the NaNs with nulls in the java code. Happy to see it merged! thanks for your work @tomekzaw

I came up with a fix for the gesture handler library to prevent NaN values in first place. Let’s see what they say about it. https://github.com/software-mansion/react-native-gesture-handler/pull/1853

folly::toJson: JSON object value was a NaN when serializing value at "velocity"->"y" keeps happening for our users. It seems that it directly happens after an orientation switch has happened and the viewport gets recreated with new dimensions. I’m wondering if this is more a problem with react-native-gesture-handler .

Would it make sense to ignore events with which contain NaN values? I’d prefer that over crashing the entire app, but this might be specific to us. @piaskowyk