Skip to content

Fix Android Fabric sync prop null override#56913

Open
jingjing2222 wants to merge 1 commit into
facebook:mainfrom
jingjing2222:fix-android-fabric-sync-props-null
Open

Fix Android Fabric sync prop null override#56913
jingjing2222 wants to merge 1 commit into
facebook:mainfrom
jingjing2222:fix-android-fabric-sync-props-null

Conversation

@jingjing2222
Copy link
Copy Markdown
Contributor

@jingjing2222 jingjing2222 commented May 21, 2026

Summary:

This fixes an Android Fabric crash in the synchronous mount props override path
used by overrideBySynchronousMountPropsAtMountingAndroid.

As-is To-be
Baseline Android Fabric transform crash repro Patched Android Fabric transform result
crashed.gif patched.gif

Repro repository:
https://github.com/jingjing2222/react-native-fabric-transform-repro

Context: we have been using overrideBySynchronousMountPropsAtMountingAndroid
enabled, following the same direction as the previous feature flag PR
#56652. While using the flag, we hit
a crash when a prop previously written by Native Animated is later cleared by a
regular Fabric props update.

The repro sequence is:

  1. useNativeDriver: true sends a native animated transform update through the
    direct manipulation path.
  2. Android stores that synchronous transform override for the view tag.
  3. React later sends a normal Fabric props update that removes transform from
    the same tag.
  4. Fabric represents that removal as transform: null.
  5. The synchronous override merge path sees the same key in the stored override
    map and in the incoming props update, then assumes the incoming value has the
    same non-null type as the stored value.

For transform, the stored value is an array, but the incoming value is null.
That currently crashes in SurfaceMountingManager.overridePropsReadableMap while
dispatching the Fabric mount item.

Observed Android logcat excerpt from the repro app:

dispatchMountItems: caught exception, displaying mount state
java.lang.AssertionError: Assertion failed
  at com.facebook.react.fabric.mounting.SurfaceMountingManager$Companion.overridePropsReadableMap(SurfaceMountingManager.kt:1195)
  at com.facebook.react.fabric.mounting.SurfaceMountingManager.updateProps(SurfaceMountingManager.kt:620)
  at com.facebook.react.fabric.mounting.mountitems.IntBufferBatchMountItem.execute(IntBufferBatchMountItem.kt:127)
  at com.facebook.react.fabric.mounting.MountItemDispatcher.dispatchMountItems(MountItemDispatcher.kt:246)
  at com.facebook.react.fabric.FabricUIManager$DispatchUIFrameCallback.doFrameGuarded(FabricUIManager.java:1528)

FATAL EXCEPTION: main
Process: com.reprocrash
java.lang.AssertionError: Assertion failed

The failing mount item is a regular Fabric props update for the same view tag:

dispatchMountItems: mountItem: UPDATE PROPS [10]: <hidden>

This patch treats an incoming null value as a real React prop removal. When the incoming Fabric props update contains a key that also exists in the stored synchronous override map, and the incoming value is null, the key is removed from the stored override map and the null update is left intact.

This is not limited to transform. It also preserves the same prop-removal semantics for opacity and any future props stored by this path. If all stored keys for a tag are cleared, the per-tag override entry is also removed.

Why this is correct

The synchronous override map is still required for normal native-driver updates. Native Animated can update the view on the UI thread before a stale regular Fabric commit arrives, and the stored override prevents that stale commit from resetting the latest animated value.

An incoming null is different from a stale animated value. It represents React removing the prop, which is the same semantic used by the JS-thread path when transform disappears from React props. In that case, the stored native override should not win; it should be dropped so the native view can reset the prop.

animated value update: stored synchronous override can win over stale Fabric props
prop removal update: incoming null wins and clears the stored override

Changelog:

[ANDROID] [FIXED] - Fix a Fabric synchronous mount props crash when a stored native animated prop is cleared by a null props update.

Test Plan:

Added regression coverage for clearing stored synchronous mount props with
incoming null Fabric updates:

  • updateProps_withNullOpacity_removesStoredSynchronousProp
  • updateProps_withNullTransform_removesStoredSynchronousProp

Ran:

FBSOURCE_ENV=1 ./gradlew -Preact.internal.useHermesStable=true :packages:react-native:ReactAndroid:testDebugUnitTest --tests com.facebook.react.fabric.SurfaceMountingManagerSynchronousMountPropsTest

Result:

BUILD SUCCESSFUL

Ran:

./gradlew -Preact.internal.useHermesStable=true ktfmtCheck

Result:

BUILD SUCCESSFUL

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label May 21, 2026
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant