Skip to content

Remove tag from allocatedViewRegistry in destroyUnmountedShadowNode (#56274)#56274

Open
javache wants to merge 1 commit intofacebook:mainfrom
javache:export-D98729251
Open

Remove tag from allocatedViewRegistry in destroyUnmountedShadowNode (#56274)#56274
javache wants to merge 1 commit intofacebook:mainfrom
javache:export-D98729251

Conversation

@javache
Copy link
Copy Markdown
Member

@javache javache commented Mar 30, 2026

Summary:

When a ShadowNode is destroyed (e.g. from a superseded concurrent render
or React Transition), destroyUnmountedShadowNode calls Java
destroyUnmountedView which deletes the view from tagToViewState. However,
the C++ allocatedViewRegistry was not updated, leaving a stale entry.

This causes a crash when a subsequent executeMount encounters a Create
mutation for the same tag: allocatedViewTags.contains(tag) returns true
(stale entry from preallocate), so the Create mount item is skipped.
But the Java view was already destroyed. When subsequent mount items
(Insert, UpdateState, etc.) try to reference the tag, getViewState()
throws RetryableMountingLayerException because the tag is not in
tagToViewState.

The fix mirrors what the normal Delete mutation path already does
(line 638: allocatedViewTags.erase(tag)) - ensuring allocatedViewRegistry
stays in sync with the Java-side tagToViewState.

Changelog: [Android][Fixed] - Fix crash from stale preallocated view registry after concurrent render cancellation

Differential Revision: D98729251

@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 Mar 30, 2026
@meta-codesync
Copy link
Copy Markdown

meta-codesync bot commented Mar 30, 2026

@javache has exported this pull request. If you are a Meta employee, you can view the originating Diff in D98729251.

@meta-codesync meta-codesync bot changed the title Remove tag from allocatedViewRegistry in destroyUnmountedShadowNode Remove tag from allocatedViewRegistry in destroyUnmountedShadowNode (#56274) Mar 30, 2026
javache added a commit to javache/react-native that referenced this pull request Mar 30, 2026
…acebook#56274)

Summary:

When a ShadowNode is destroyed (e.g. from a superseded concurrent render
or React Transition), destroyUnmountedShadowNode calls Java
destroyUnmountedView which deletes the view from tagToViewState. However,
the C++ allocatedViewRegistry was not updated, leaving a stale entry.

This causes a crash when a subsequent executeMount encounters a Create
mutation for the same tag: allocatedViewTags.contains(tag) returns true
(stale entry from preallocate), so the Create mount item is skipped.
But the Java view was already destroyed. When subsequent mount items
(Insert, UpdateState, etc.) try to reference the tag, getViewState()
throws RetryableMountingLayerException because the tag is not in
tagToViewState.

The fix mirrors what the normal Delete mutation path already does
(line 638: allocatedViewTags.erase(tag)) - ensuring allocatedViewRegistry
stays in sync with the Java-side tagToViewState.

Changelog: [Android][Fixed] - Fix crash from stale preallocated view registry after concurrent render cancellation

Differential Revision: D98729251
javache added a commit to javache/react-native that referenced this pull request Mar 30, 2026
…acebook#56274)

Summary:

When a ShadowNode is destroyed (e.g. from a superseded concurrent render
or React Transition), destroyUnmountedShadowNode calls Java
destroyUnmountedView which deletes the view from tagToViewState. However,
the C++ allocatedViewRegistry was not updated, leaving a stale entry.

This causes a crash when a subsequent executeMount encounters a Create
mutation for the same tag: allocatedViewTags.contains(tag) returns true
(stale entry from preallocate), so the Create mount item is skipped.
But the Java view was already destroyed. When subsequent mount items
(Insert, UpdateState, etc.) try to reference the tag, getViewState()
throws RetryableMountingLayerException because the tag is not in
tagToViewState.

The fix mirrors what the normal Delete mutation path already does
(line 638: allocatedViewTags.erase(tag)) - ensuring allocatedViewRegistry
stays in sync with the Java-side tagToViewState.

Changelog: [Android][Fixed] - Fix crash from stale preallocated view registry after concurrent render cancellation

Differential Revision: D98729251
…acebook#56274)

Summary:
Pull Request resolved: facebook#56274

When a ShadowNode is destroyed (e.g. from a superseded concurrent render
or React Transition), destroyUnmountedShadowNode calls Java
destroyUnmountedView which deletes the view from tagToViewState. However,
the C++ allocatedViewRegistry was not updated, leaving a stale entry.

This causes a crash when a subsequent executeMount encounters a Create
mutation for the same tag: allocatedViewTags.contains(tag) returns true
(stale entry from preallocate), so the Create mount item is skipped.
But the Java view was already destroyed. When subsequent mount items
(Insert, UpdateState, etc.) try to reference the tag, getViewState()
throws RetryableMountingLayerException because the tag is not in
tagToViewState.

The fix mirrors what the normal Delete mutation path already does
(line 638: allocatedViewTags.erase(tag)) - ensuring allocatedViewRegistry
stays in sync with the Java-side tagToViewState.

Changelog: [Android][Fixed] - Fix crash from stale preallocated view registry after concurrent render cancellation

Differential Revision: D98729251
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. fb-exported meta-exported p: Facebook Partner: Facebook Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant