[Android] ANR: synchronouslyUpdateUIProps prints full stack traces on the UI thread when the view tag is unmounted (RN 0.86 reflection path)

Author: mikelrosCreated Aug 14, 2026Updated Sep 16, 2026
LabelsMissing reproPlatform: Android

[!NOTE] This issue was authored by AI on behalf of @mikelros.

Description

We received a production ANR cluster (Android, release builds, ~150 users in two weeks) whose main-thread stack ends inside Reanimated's exception logging during the draw pass:

at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:6856)
...
at com.horcrux.svg.SvgView.onDraw(SvgView.java:135)
...
at com.horcrux.svg.VirtualView.setClientRect(VirtualView.java:614)
at com.facebook.react.uimanager.events.FabricEventDispatcher.dispatchEvent(FabricEventDispatcher.kt:46)
at com.swmansion.reanimated.NodesManager.onEventDispatch(NodesManager.kt:212)
at com.swmansion.reanimated.NodesManager.performOperationsRespectingDrawPass(NodesManager.kt:132)
at com.swmansion.reanimated.NodesManager.performNonLayoutOperations(NodesManager.kt:126)
at com.swmansion.reanimated.NativeProxy.performNonLayoutOperations(NativeProxy.kt)
at com.swmansion.reanimated.NativeProxy.synchronouslyUpdateUIProps(NativeProxy.kt:253)
at com.swmansion.reanimated.nativeProxy.SynchronousPropsBufferParser.parse(SynchronousPropsBufferParser.kt:257)
at com.swmansion.reanimated.NativeProxy.synchronouslyUpdateUIProps$lambda$6(NativeProxy.kt:258)
at io.sentry.android.core.SentryLogcatAdapter.w(SentryLogcatAdapter.java:115)
at android.util.Log.w(Log.java:229)
at android.util.Log.printlns(Log.java:500)
at java.lang.Throwable.printStackTrace(Throwable.java:753)
...

Code-path analysis

The code below is from 4.5.3, but main has the same shape today.

  1. On React Native 0.86, NativeProxy.synchronouslyUpdateUIProps takes the new reflection path into MountingManager.updatePropsSynchronously:

    kotlin
    // NativeProxy.kt:253-264
    SynchronousPropsBufferParser.parse(intBuffer, doubleBuffer) { viewTag, props ->
        if (BuildConfig.IS_REACT_NATIVE_86_OR_NEWER) {
            try {
                updatePropsSynchronouslyMethod.invoke(mountingManager, viewTag, props)
            } catch (e: Exception) {
                Log.w("Reanimated", "synchronouslyUpdateUIProps failed for tag $viewTag", e)
            }
        } else { ... }
    }
  2. MountingManager.updatePropsSynchronously throws RetryableMountingLayerException when the tag has no SurfaceMountingManager (the view was unmounted). The vanilla RN path (FabricUIManager.synchronouslyUpdateViewOnUIThread) checks getViewExists(reactTag) first and defers quietly — it never throws for a missing tag. The reflection path skips that guard.

  3. The catch logs the full throwable on the UI thread. The chain is InvocationTargetException + cause (~160 frames in our app), printed via Throwable.printStackTrace inside the draw pass, once per stale tag, and again on every following frame because the update is re-scheduled. Apps that observe logcat (Sentry's SentryLogcatAdapter breadcrumbs in our case) pay extra on top. The main thread spends whole seconds in Log.printlns and Android raises an ANR.

  4. The synchronous re-entry during draw comes from react-native-svg: VirtualView.setClientRect dispatches its layout event from inside onDraw, which enters performOperationsRespectingDrawPassperformNonLayoutOperationssynchronouslyUpdateUIProps. Any other in-draw synchronous dispatch would do the same; svg only supplies the trigger.

Expected behavior

A prop update for an unmounted tag should be dropped (or deferred) silently, as the vanilla RN path does — not logged with a full stack print per tag per frame on the UI thread. We ship this as a patch-package patch and it removes the stall:

kotlin
private val getViewExistsMethod by lazy {
    mountingManager.javaClass.methods
        .first { it.name == "getViewExists" && it.parameterTypes.size == 1 }
        .apply { isAccessible = true }
}

// in synchronouslyUpdateUIProps:
try {
    if (getViewExistsMethod.invoke(mountingManager, viewTag) == true) {
        updatePropsSynchronouslyMethod.invoke(mountingManager, viewTag, props)
    }
} catch (e: Exception) {
    Log.w("Reanimated", "synchronouslyUpdateUIProps failed for tag $viewTag: $e")
}

Happy to open a PR in this shape.

Occurrence details

  • Two Sentry ANR groups (foreground + background), 163 events / ~150 users in two weeks, all starting with our first RN 0.86 + Reanimated 4.5.3 release. Identical main-thread stack in every sampled event.
  • Devices: broad spread (Samsung, Xiaomi, others), Android 14–16, arm64-v8a, Play Store installs, not rooted.
  • App: React Native 0.86.2 (New Architecture), Hermes, Expo SDK 57 (bare / dev-client workflow), release builds, expo-updates enabled.

Steps to reproduce

  1. Render a screen where an SVG view (react-native-svg 15.x) draws while a Reanimated shared value updates props of a view that has since unmounted (a list item with an animated style that leaves the window keeps its shared value updating).
  2. During the draw pass, the svg layout event re-enters Reanimated, synchronouslyUpdateUIProps runs, the reflective updatePropsSynchronously throws RetryableMountingLayerException for the stale tag, and a full stack trace prints to logcat.
  3. Watch logcat fill with synchronouslyUpdateUIProps failed for tag ... warnings once per frame; with enough stale tags (or a logcat observer attached) the main thread stalls past the ANR threshold.

Snack or a link to a repository

No minimal repro yet — it needs a stale animated tag, which depends on app-specific unmount timing. The defect is visible by inspection: any RetryableMountingLayerException from the reflective call is logged with a full UI-thread stack print, per tag, per frame, with no back-off, while the equivalent RN path (FabricUIManager.synchronouslyUpdateViewOnUIThread) never throws for missing tags.

Reanimated version

4.5.3

Worklets version

0.11.3

React Native version

0.86.2

Platforms

Android

JavaScript runtime

Hermes

Workflow

Expo Dev Client (bare workflow)

Architecture

New Architecture (Fabric renderer)

Source: software-mansion/react-native-reanimated