Skip to content

fix(android): fix FlatList crash when view loses and regains native view in merged Fabric transaction - #59761

Open
parthrana-27 wants to merge 1 commit into
react:mainfrom
parthrana-27:fix/59724-flatlist-crash-delete-order-android
Open

parthrana-27 wants to merge 1 commit into
react:mainfrom
parthrana-27:fix/59724-flatlist-crash-delete-order-android

Conversation

@parthrana-27

Copy link
Copy Markdown

Summary:

Fixes #59724

On Android, a FlatList (or any component using Fabric) can crash with:

java.lang.IllegalStateException: Check failed.
  at com.facebook.react.views.view.ReactViewGroup.isViewClipped(ReactViewGroup.kt:767)
  at com.facebook.react.views.view.ReactViewGroup.updateSubviewClipStatus(ReactViewGroup.kt:562)
  ...

when a View loses its native view and gets it back within the same JavaScript task — for example when a list item is unmounted and immediately remounted due to rapid state changes on a low-end Android device.

Root cause

FabricMountingManager::executeMount separates Delete mount items from the rest and writes them last as a space-efficiency optimisation. A comment in the original code acknowledged the problem:

// FIXME: this optimization is incorrect when multiple transactions are
// merged together

When two transactions are merged, the same tag can appear in a Delete mutation (from the earlier transaction) and a Create mutation (from the later transaction) inside the same merged batch. Because deletes are written last, the Java side receives a layout Update for the tag while the old native view is still attached to a different ReactViewGroup. When that view's parent is later checked in isViewClipped, check(parent === this) fails with IllegalStateException.

Fix (two parts)

1. FabricMountingManager.cpp — root cause

In the Delete mutation branch of executeMount, scan the current batch for a subsequent Create with the same tag. If one exists, promote the Delete into cppCommonMountItems so it is emitted in mutation order (before the Create), rather than being deferred to the end. For the common case where no tag is recycled in the same batch, the delete-last optimisation is fully preserved.

2. ReactViewGroup.kt — defensive guard

Replace the hard check(parent === this) in isViewClipped with a soft-exception path: when parent !== this, log a ReactNoCrashSoftException (surfaced in crash reporting) and return true (treat the view as clipped/removed). This prevents a fatal crash for any ordering edge-cases not yet covered by part 1, and is consistent with the logSoftException already present a few lines above in the same function.

Changelog:

[ANDROID] [FIXED] - Fix IllegalStateException crash in ReactViewGroup.isViewClipped when a FlatList item loses and regains its native view within a merged Fabric transaction batch

Test Plan:

Reproducer: https://github.com/Nezz/expo-repro/tree/repro/MergedTransactionDeleteOrder

  1. Clone the reproducer, run npm install.
  2. Build: cd android && ./gradlew assembleRelease -PreactNativeArchitectures=arm64-v8a
  3. adb install -r app/build/outputs/apk/release/app-release.apk
  4. Open the app and tap "one task".

Without this fix: the app crashes immediately with IllegalStateException: Check failed at ReactViewGroup.isViewClipped.

With this fix: the app no longer crashes.

Unit tests: The fix is in native C++ / Kotlin and does not change any JS or TypeScript surface. The existing ReactViewGroup unit tests pass unchanged. No new test infrastructure is required — the reproducer above is the regression test.

Static analysis: ruff/clang-format not applicable; the C++ change follows the surrounding code style and the Kotlin change matches the existing logSoftException pattern already present in isViewClipped.

@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 Nov 2, 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 Nov 2, 2026
@parthrana-27
parthrana-27 force-pushed the fix/59724-flatlist-crash-delete-order-android branch from bf302d0 to 60ada85 Compare November 2, 2026 15:15
When multiple Fabric transactions are merged, a tag can appear in both
a DELETE mutation (from an earlier transaction) and a CREATE mutation
(from a later transaction) within the same merged batch.

The existing optimisation writes all Delete mount items last (after
Updates and Creates) for space efficiency.  This is safe when a single
transaction is processed, because the differ guarantees DELETE always
precedes CREATE for the same tag within one transaction.  However,
when transactions are merged the guarantee no longer holds: DELETEs
from the earlier transaction land after CREATEs from the later one,
so the Java side receives layout UPDATE instructions for a tag whose
native view has already been re-created and re-parented.  That
triggers an IllegalStateException in ReactViewGroup.isViewClipped
(check failed: parent !== this).

Fix (two-part):

1. FabricMountingManager.cpp (root cause)
   In the Delete mutation branch of executeMount, detect whether a
   CREATE for the same tag exists later in the same batch.  If so,
   promote the DELETE into cppCommonMountItems (which preserves
   mutation order) instead of deferring it to cppDeleteMountItems.
   For the common case (no recycled tag) the delete-last optimisation
   is preserved unchanged.

2. ReactViewGroup.kt (defensive guard)
   Replace the hard crash check(parent === this) in isViewClipped with
   a soft-exception path that logs the anomaly and returns true
   (treating the view as clipped) when parent !== this.  This ensures
   any similar ordering edge-cases in the future degrade gracefully
   rather than producing a fatal crash.

Fixes react#59724
@parthrana-27
parthrana-27 force-pushed the fix/59724-flatlist-crash-delete-order-android branch from 60ada85 to 0d88937 Compare November 3, 2026 06:08

This branch has not been deployed

No deployments
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.

[Android] FlatList crashes when a View loses its native view and gets it back

1 participant