Skip to content

Commit 7a546f9

Browse files
Gate printMountItem in error handlers behind enableFabricLogs (#56012)
Summary: Two `printMountItem()` call sites in `MountItemDispatcher.kt` error handlers are not gated behind `ReactNativeFeatureFlags.enableFabricLogs()`, while every other `printMountItem` call site in the same file is. When a Fabric mount operation fails under memory pressure, the unguarded error handler calls `toString()` on every mount item in the batch — each using `String.format()` — which triggers a fatal `OutOfMemoryError` (Android 256MB heap limit). The device is already low on memory when the original mount error occurs, so allocating diagnostic strings for the entire batch is the killing blow. **Impact:** 67 affected users in production, 87% Android 16, all Samsung devices. <details> <summary>Production stacktrace (67 occurrences)</summary> ``` java.lang.OutOfMemoryError: Failed to allocate a 48 byte allocation with 2222800 free bytes and 2170KB until OOM, target footprint 268435456, growth limit 268435456; giving up on allocation because <1% of heap free after GC. at java.util.Formatter.parse(Formatter.java:2737) at java.util.Formatter.format(Formatter.java:2686) at java.util.Formatter.format(Formatter.java:2640) at java.lang.String.format(String.java:4489) at com.facebook.react.fabric.mounting.mountitems.IntBufferBatchMountItem.toString(IntBufferBatchMountItem.kt:220) at com.facebook.react.fabric.mounting.MountItemDispatcher$Companion.printMountItem(MountItemDispatcher.kt:358) at com.facebook.react.fabric.mounting.MountItemDispatcher$Companion.access$printMountItem(MountItemDispatcher.kt:337) at com.facebook.react.fabric.mounting.MountItemDispatcher.dispatchMountItems(MountItemDispatcher.kt:230) at com.facebook.react.fabric.mounting.MountItemDispatcher.tryDispatchMountItems(MountItemDispatcher.kt:88) at com.facebook.react.fabric.FabricUIManager$DispatchUIFrameCallback.doFrameGuarded(FabricUIManager.java:1484) at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.kt:25) at com.facebook.react.modules.core.ReactChoreographer.frameCallback$lambda$1(ReactChoreographer.kt:59) ``` Device: Samsung SM-S938B (Galaxy S25 Ultra), Android 16, 12GB RAM Heap limit: 256MB, <1% free after GC </details> **Fix:** Gate the two remaining `printMountItem` call sites behind `enableFabricLogs()`, matching the existing pattern used at all other call sites in the same file. ## Changelog: [ANDROID] [FIXED] - Gate diagnostic `printMountItem` calls in `MountItemDispatcher` error handlers behind `enableFabricLogs()` to prevent OOM crash Pull Request resolved: #56012 Test Plan: Applied as a patch to a production app. OOM crash from the unguarded error handler path no longer occurs — confirmed over several weeks with 67 previously affected users. The change is strictly additive gating — no logic changes. All other `printMountItem` call sites in the same file already use the same `enableFabricLogs()` guard, so this just makes the two error-handler sites consistent. Reviewed By: javache, shwanton Differential Revision: D97414011 Pulled By: alanleedev fbshipit-source-id: 49818d58cacc7a164f71f33118a74e2b75dae6bc
1 parent 320e3bd commit 7a546f9

1 file changed

Lines changed: 8 additions & 6 deletions

File tree

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ internal class MountItemDispatcher(
120120
// items
121121
addViewCommandMountItem(mountItem)
122122
}
123-
} else {
123+
} else if (ReactNativeFeatureFlags.enableFabricLogs()) {
124124
printMountItem(item, "dispatchExternalMountItems: mounting failed with ${e.message}")
125125
}
126126
}
@@ -247,12 +247,14 @@ internal class MountItemDispatcher(
247247
} catch (e: Throwable) {
248248
// If there's an exception, we want to log diagnostics in prod and rethrow.
249249
FLog.e(TAG, "dispatchMountItems: caught exception, displaying mount state", e)
250-
for (m in items) {
251-
if (m === mountItem) {
252-
// We want to mark the mount item that caused exception
253-
FLog.e(TAG, "dispatchMountItems: mountItem: next mountItem triggered exception!")
250+
if (ReactNativeFeatureFlags.enableFabricLogs()) {
251+
for (m in items) {
252+
if (m === mountItem) {
253+
// We want to mark the mount item that caused exception
254+
FLog.e(TAG, "dispatchMountItems: mountItem: next mountItem triggered exception!")
255+
}
256+
printMountItem(m, "dispatchMountItems: mountItem")
254257
}
255-
printMountItem(m, "dispatchMountItems: mountItem")
256258
}
257259

258260
if (mountItem.getSurfaceId() != View.NO_ID) {

0 commit comments

Comments
 (0)