You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Android] A RectButton with Reanimated enabled and a worklet callback ends the app at its first touch #4628
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
Render the component from the gist.
Tap the first button, a RawButton with a worklet onTouchesDown: it logs.
Tap the second, a RectButton with only onPress: it logs.
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.
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.
Activity
m-bert commentedon Oct 27, 2026
Hi @YevheniiKotyrlo! Thanks for the detailed report and the gist.
The cause is what you described. A
workletcallback moves the gesture onto the Reanimated detector, andBaseButton(soRectButtonandBorderlessButtontoo) 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
disableReanimatedandrunOnJSare removed from their props. Aworkletcallback 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.Remove `disableReanimated` prop from components (#4650)