From 641e30035637997f5eaa2c857a7bad76d9329b25 Mon Sep 17 00:00:00 2001 From: Racer Engineer Date: Wed, 8 Apr 2026 10:17:17 -0700 Subject: [PATCH] Guard against measuring detached views in SurfaceMountingManager.updateLayout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: ## Summary: Catch `IllegalStateException` from `viewToUpdate.measure()` in `SurfaceMountingManager.updateLayout()` instead of skipping the entire layout update. This fixes a crash where Jetpack Compose's `onMeasure` throws when a `ComposeView` is detached from the window hierarchy, while ensuring `layout()`, visibility, and parent `requestLayout()` still execute normally. **Root cause:** RN Fabric dispatches layout updates via Choreographer. If a `ComposeView` was detached, its `onMeasure` attempts to access `windowRecomposer`, throwing `IllegalStateException`. **Previous approach (D99770236v1):** Used `isAttachedToWindow` guard to skip the entire method. This caused an E2E test regression — views created during Fabric's batch mount could receive layout updates before being attached, leaving them with zero dimensions. **New approach:** Wrap only the `measure()` call in a try-catch for `IllegalStateException`, logging via `ReactSoftExceptionLogger`. The `layout()` call and all other state updates proceed normally, preserving correct view dimensions for all views. Related: T263517828 (original crash task), D97087668 (similar guard in `updateProps`) --- ## Instructions about RACER Diffs: **Commandeer this diff (recommended) or land with accept2ship tag.** **This diff was generated by Racer AI agent on behalf of [William Khoe](https://www.internalfb.com/profile/view/3409840) for T263534248. If the diff quality is poor, consider contacting the user to provide clearer instructions on the task.** - If you are happy with the changes, commandeer it if minor edits are needed. (**we encourage commandeer to get the diff credit**) - If you are not happy with the changes, please comment on the diff with clear actions and send it back to the author. Racer will pick it up and re-generate. - If you really feel the Racer is not helping with this change (alas, some complex changes are hard for AI) feel free to abandon this diff. - **For M10N reviewers:** as you review AI-generated diffs, we ask you to give them the same priority as human-generated diffs, and take action in a timely manner by either accepting, rejecting, or resigning as a reviewer. For diffs that don't meet the quality bar (e.g. code doesn't compile, not readable or introduces functionality regressions), we ask that you use the following hashtags to provide clear signals to improve our tools - `#monlowqualitydiff` `#monwrongreviewerdiff` --- > Generated by [RACER](https://www.internalfb.com/wiki/RACER_(Risk-Aware_Code_Editing_and_Refactoring)/), powered by [Confucius](https://www.internalfb.com/wiki/Confucius/Analect/Shared_Analects/Confucius_Code_Assist_(CCA)/) [Session](https://www.internalfb.com/confucius?session_id=634a2946-3236-11f1-9c44-99a80a63f54d&tab=Chat), [Trace](https://www.internalfb.com/confucius?session_id=634a2946-3236-11f1-9c44-99a80a63f54d&tab=Trace) Reviewed By: cortinico Differential Revision: D99770236 --- .../fabric/mounting/SurfaceMountingManager.kt | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.kt index 485dd80d02f1..344fef5e16fa 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.kt @@ -779,10 +779,20 @@ internal constructor( // because of caching and calculation that may occur in onMeasure and onLayout. Layout // operations should also follow the native view hierarchy and go top to bottom for // consistency with standard layout passes (some views may depend on this). - viewToUpdate.measure( - View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), - View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY), - ) + try { + viewToUpdate.measure( + View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY), + ) + } catch (e: IllegalStateException) { + ReactSoftExceptionLogger.logSoftException( + TAG, + ReactNoCrashSoftException( + "measure() threw IllegalStateException for tag $reactTag (view not attached to window)", + e, + ), + ) + } // We update the layout of the RootView when there is a change in the layout of its child. This // is required to re-measure the size of the native View container (usually a FrameLayout) that