Skip to content

[Android] A RectButton with Reanimated enabled and a worklet callback ends the app at its first touch #4628

Closed

Description

@YevheniiKotyrlo

Description

A RectButton with disableReanimated={false} and a worklet callback, such as onTouchesDown, ends the app at its first touch:

com.facebook.jni.CppException: [Worklets] Tried to synchronously call a Remote Function. Called "anonymous" on the UI Runtime.
    at runCallback_eventHandlersUtilsTs3
    at handleStateChangeEvent_eventHandlerTs1

A worklet callback puts the gesture on the Reanimated detector (configUtils.ts#L80-L84), which runs its callbacks on the UI runtime, and BaseButton, which RectButton renders, hands RawButton its own onBegin, onActivate, onDeactivate and onFinalize, none of them worklets (GestureButtons.tsx#L57-L109). A RawButton with a worklet callback, and a RectButton with disableReanimated={false} and no worklet callback, work.

Steps to reproduce

  1. Render the component from the gist.
  2. Tap the first button, a RawButton with a worklet onTouchesDown: it logs.
  3. Tap the second, a RectButton with only onPress: it logs.
  4. Tap the third, a RectButton with a worklet onTouchesDown: the app ends with the error above.

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

https://gist.github.com/YevheniiKotyrlo/7c3d7ec24997076765f8a36d5bddcf09

Gesture Handler version

main at 40ac206; 3.4.0 has the same code

React Native version

0.88.0

Platforms

Android

JavaScript runtime

Hermes

Workflow

Using Expo Prebuild or an Expo development build

Build type

Release mode

Device

Android emulator

Device model

API 34 emulator (x86_64, Google APIs), Android 14

Acknowledgements

Yes

Activity

  1. m-bert commented on Oct 27, 2026

    @m-bert
    Collaborator

    Hi @YevheniiKotyrlo! Thanks for the detailed report and the gist.

    The cause is what you described. A worklet callback moves the gesture onto the Reanimated detector, and BaseButton (so RectButton and BorderlessButton too) passes its own plain JS callbacks to the inner button, which then end up running on the UI runtime.

    This is not a bug we are going to fix by making worklet callbacks work on these components. They were never meant to support them, so instead we are making that explicit: wrapped components now always run their callbacks on the JS thread, and disableReanimated and runOnJS are removed from their props. A worklet callback will no longer crash, but it will run on the JS thread, not the UI thread. The docs now say so. It's changed in #4650.

  2. added a commit that references this issue on Oct 28, 2026
    4e18d5f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions