-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
Description
On Android with Fabric, calling measure(animatedRef) before the underlying native view is attached/laid out crashes the app with a SIGSEGV inside React Native’s UIManager. This occurs even in debug builds. It’s not specific to keyboards; any timing/race that calls measure too early can reproduce it.
Environment
• React Native: 0.79.5
• react-native-reanimated: 3.19.1
• Build type: Debug (Fabric enabled)
Hypothesized Triggering Flow
1. JS: Declare animatedRef on an Animated.View.
2. JS: In useLayoutEffect, register a worklet callback (via REA Core registerEventHandler or native module binding) that calls measure(animatedRef).
3. Native: A native module fires an event immediately after handler registration (e.g., onStart/onProgress from react-native-keyboard-controller / WindowInsetsAnimation), before the view’s node is attached/la id out.
4. Worklet runs → measure(animatedRef) → RN UIManager::getRelativeLayoutMetrics → SIGSEGV (null deref of ShadowNode/Surface).
Attempted (Failed) Repro
Here's my FAILED attempt at a repro. It's conceptually similar to what is happening in my app. But I must be missing some key ingredient because it does not in fact trigger a crash:
let element: ReactNode = null;
let callback = () => {};
export function PouchesOverview() {
const storeData = useSyncExternalStore(
(fn) => {
callback = fn;
return () => {};
},
() => element,
);
return (
<View>
<Button
title="Trigger Crash"
onPress={() => {
Keyboard.dismiss(); //Begin emitting move events...
element = <Crash />;
callback();
}}
/>
<View style={{ flexDirection: "row" }}>
<TryCrash />
{storeData}
</View>
</View>
);
}
function TryCrash() {
useKeyboardHandler({
onMove: () => {
"worklet";
},
});
return (
<View style={{ flex: 1 }}>
<TextInput style={{ backgroundColor: "pink", borderRadius: 9999 }} />
<CrashInner />
</View>
);
}
function TryCrashInner() {
const ref1 = useAnimatedRef<View>();
const ref2 = useAnimatedRef<View>();
useKeyboardHandler({
onMove: () => {
"worklet";
measure(ref1);
measure(ref2);
},
});
return (
<View style={{ flex: 1 }}>
<Animated.View ref={ref1}>
<Animated.View
ref={ref2}
style={{ flex: 1, height: 50, width: 50, backgroundColor: "red", borderRadius: 999 }}
/>
</Animated.View>
{Array(200)
.fill(0)
.map((__, i) => (
<View key={i} style={{ height: 50, width: 50, backgroundColor: "blue" }}>
<Text>{Math.random()}</Text>
</View>
))}
</View>
);
}
Native Logs
I do however have some pretty decent tombstone data:
F/libc : Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000000000000058
F/DEBUG : *** *** *** *** *** *** *** *** *** *** *** ***
F/DEBUG : Build fingerprint: 'google/sdk_gphone64_arm64/emu64a:14/UE1A.230829.036/11036701:user/release-keys'
F/DEBUG : ABI: 'arm64'
F/DEBUG : Timestamp: 2025-09-24 11:04:12.054223612-0600
F/DEBUG : Process uptime: 10s
F/DEBUG : Cmdline: money.roo.app
F/DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x58
F/DEBUG : Cause: null pointer dereference
Registers (excerpt):
F/DEBUG : x0 0000000000000058 ... lr 0000007584ac7f70 sp 0000007ff6bc9f10 pc 000000758468c6d0
And some top native frames showing the null deref:
#00 pc 0x... /base.apk!libreactnative.so (...offset...) <-- crash
#01 pc 0x... /base.apk!libreactnative.so facebook::react::ShadowNode::getSurfaceId() const+24
#02 pc 0x... /base.apk!libreactnative.so facebook::react::UIManager::getRelativeLayoutMetrics(...)+124
#03 pc 0x... /base.apk!libreanimated.so reanimated::ReanimatedModuleProxy::measure(...)+176
#04-#18 /base.apk!libreanimated.so (worklets::jsi_utils / host function plumbing)
#19-#27 /base.apk!libworklets.so (RuntimeDecorator / WithRuntimeDecorator / call chain)
#34-#59 /base.apk!libhermes.so (Hermes frames around host call)
Event/dispatch path indicating the native-side keyboard/insets source and Fabric events:
#70 pc ... libworklets.so worklets::EventHandlerRegistry::processEvent(...)
#71 pc ... libreanimated.so ReanimatedModuleProxy::handleEvent(...)
#72 pc ... libreanimated.so NativeProxy::handleEvent(...)
#80 pc ... libreanimated.so reanimated::EventHandler::receiveEvent(...)
#83-#84 ... libreanimated.so JNI MethodWrapper/FunctionWrapper dispatch
#90 pc ... <anonymous> com.swmansion.reanimated.nativeProxy.EventHandler.receiveEvent+0
#95 pc ... <anonymous> com.facebook.react.uimanager.events.Event.dispatch+0
#110 pc ... <anonymous> com.facebook.react.uimanager.events.FabricEventDispatcher.dispatchEvent+0
#115 pc ... <anonymous> com.reactnativekeyboardcontroller.extensions.ThemedReactContextKt.dispatchEvent+0
#120 pc ... <anonymous> com.reactnativekeyboardcontroller.listeners.KeyboardAnimationCallback.onProgress+0
#125 pc ... <anonymous> androidx.core.view.WindowInsetsAnimationCompat$Impl30$ProxyCallback.onProgress+0
These frames show:
• A native event (KeyboardAnimationCallback.onProgress → REA native proxy/event handler) runs a worklet.
• The worklet invokes ReanimatedModuleProxy::measure.
• RN UIManager::getRelativeLayoutMetrics calls into ShadowNode::getSurfaceId() on a node that isn’t attached → SIGSEGV.
Expected behavior
If the referenced node isn’t attached/la id out, measure(animatedRef) should not crash:
• Return null / throw a JS exception / or log a warning—but never segfault.
Proposed fix (high-level)
In Reanimated’s measure implementation for Fabric:
1. Resolve the ref → ShadowNode.
2. Check attachment/SurfaceId validity before calling UIManager::getRelativeLayoutMetrics.
3. If not attached (no valid SurfaceId), short-circuit back to JS with null/undefined (or a controlled error).
Workarounds until fixed
• Gate measure with an attachment/layout flag set in onLayout (shared value), and ensure native event handlers check that flag before calling measure.
• Optionally defer first measure by a frame (requestAnimationFrame) and skip while transient animations (e.g., keyboard/insets) are in progress.
Steps to reproduce
See above
Snack or a link to a repository
Reanimated version
3.19.1
Worklets version
3.19.1
React Native version
79.1
Platforms
Android
JavaScript runtime
Hermes
Workflow
Expo Dev Client
Architecture
New Architecture (Fabric renderer)
Build type
Debug app & dev bundle
Device
Android emulator
Host machine
macOS
Device model
No response
Acknowledgements
Yes