Description
Under the New Architecture (Fabric), react-native-screens mounts one native Fragment per pushed screen (ScreenStackFragment, each with a nested childFragmentManager/ScreenFragment). On a deep push-navigation stack these accumulate in the host Activity's saved state. When the OS stops the Activity (app backgrounded), onSaveInstanceState serialises all of them, and in a real app the parcel exceeds the Binder transaction budget → fatal, unhandled android.os.TransactionTooLargeException on activityStopped.
This is a save-time crash, distinct from the well-known restore-time crashes (#17, #3317) that AutoRemovingFragment / RNScreensFragmentFactory / super.onCreate(null) address. Those do not help here: the oversized parcel is rejected by the Binder at save time, before any restore.
Production crash evidence. Real TooLargeTool-style breakdown from a shipping app on this stack (the parcel that crashed at activityStopped, ~639 KB):
androidx.lifecycle.BundlableSavedStateRegistry.key [637 KB]
android:support:activity-result [208 KB]
android:support:fragments [428 KB] <- ~150 ScreenStackFragment/ScreenFragment entries
fragment_<uuid> [2868]
childFragmentManager [2176]
fragment_<uuid> [1752]
childFragmentManager [1068]
... (x150)
- Device: Samsung Galaxy A57 (SM-A576B), Android 16, 7.8 GB RAM — not low-memory.
- Each real fragment contributes ~2.8 KB (FragmentManager metadata + the nested
childFragmentManager); at 150+ retained fragments this is 428 KB.
About the reproduction (please read). The linked repo demonstrates the accumulation mechanism — it auto-pushes a native-stack screen and logs, on background, the saved-state parcel size and that android:support:fragments is present (regHasFragments=true). It does not, by itself, cross the ~1 MB limit and crash, and that is worth stating plainly: with trivial screens the saved state is only ~75 bytes/fragment (measured on API 34), so a toy stack stays far under the limit. The production crash is emergent from real screens × deep navigation (real screens save ~2.8 KB/fragment; long sessions reach 150+ retained fragments). The repo shows the code path and lets you watch the parcel grow with depth; the crash-scale evidence is the production bundle above.
Why this seems in scope for react-native-screens. RNS already declares its fragments non-restorable — it discards restored fragment state and rebuilds from JS (AutoRemovingFragment, and the discussion in #96). But it still lets that state be saved into the Activity parcel, where on deep stacks it triggers TransactionTooLargeException. Since RNS never consumes the saved fragment state, arguably it should not let it balloon the save parcel — e.g. opt its ScreenStackFragments out of instance-state saving, or expose a supported way to bound it.
App-side workaround (and a gotcha). Stripping the fragment state in MainActivity.onSaveInstanceState stops the crash — RNS rebuilds fine from JS. Note: on androidx.fragment 1.8.x the FragmentManager state is saved via SavedStateRegistry, so it is nested — the classic top-level outState.remove("android:support:fragments") is a no-op (measured: 0 bytes removed). It must be reached inside the registry bundle:
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.getBundle("androidx.lifecycle.BundlableSavedStateRegistry.key")
?.remove("android:support:fragments")
}
This depends on internal androidx key strings, which is why a first-party RNS option would be preferable.
Steps to reproduce
Reproduction repo: https://github.com/MisterMunchkin/rns-4487-repro
git clone https://github.com/MisterMunchkin/rns-4487-repro && cd rns-4487-repro && npm install
npx expo prebuild --platform android --clean then npx expo run:android on an Android 13+ emulator/device. New Architecture is enabled via app.json; a parcel-size logger is injected into MainActivity by plugins/with-parcel-logger.js.
- In a second terminal:
adb logcat -s RNSRepro.
- The app auto-pushes a self-referential native-stack screen to
TARGET_DEPTH. When it settles, enable Developer Options → "Don't keep activities" (forces the save+stop deterministically on an emulator; production hits it via natural memory pressure), then press Home.
RNSRepro logs onSaveInstanceState parcel=<N> bytes | regHasFragments=true — confirming the react-native-screens fragments accumulate in the Activity's saved state (nested under androidx.lifecycle.BundlableSavedStateRegistry.key). Raise TARGET_DEPTH (or run against content-heavy screens) to watch the parcel climb toward the Binder limit; at production scale (real screens, 150+ fragments ≈ 428 KB) it crosses ~639 KB and throws TransactionTooLargeException at activityStopped.
Snack or a link to a repository
https://github.com/MisterMunchkin/rns-4487-repro
Screens version
4.18.0
React Native version
0.81.5
Platforms
Android
JavaScript runtime
Hermes
Workflow
Expo bare workflow (expo prebuild / CNG; androidx.fragment 1.8.9)
Device
Real device (production crash) + Android emulator (mechanism)
Device model
Samsung Galaxy A57 (SM-A576B), Android 16
Acknowledgements
Yes
Description
Under the New Architecture (Fabric), react-native-screens mounts one native
Fragmentper pushed screen (ScreenStackFragment, each with a nestedchildFragmentManager/ScreenFragment). On a deep push-navigation stack these accumulate in the hostActivity's saved state. When the OS stops the Activity (app backgrounded),onSaveInstanceStateserialises all of them, and in a real app the parcel exceeds the Binder transaction budget → fatal, unhandledandroid.os.TransactionTooLargeExceptiononactivityStopped.This is a save-time crash, distinct from the well-known restore-time crashes (#17, #3317) that
AutoRemovingFragment/RNScreensFragmentFactory/super.onCreate(null)address. Those do not help here: the oversized parcel is rejected by the Binder at save time, before any restore.Production crash evidence. Real
TooLargeTool-style breakdown from a shipping app on this stack (the parcel that crashed atactivityStopped, ~639 KB):childFragmentManager); at 150+ retained fragments this is 428 KB.About the reproduction (please read). The linked repo demonstrates the accumulation mechanism — it auto-pushes a native-stack screen and logs, on background, the saved-state parcel size and that
android:support:fragmentsis present (regHasFragments=true). It does not, by itself, cross the ~1 MB limit and crash, and that is worth stating plainly: with trivial screens the saved state is only ~75 bytes/fragment (measured on API 34), so a toy stack stays far under the limit. The production crash is emergent from real screens × deep navigation (real screens save ~2.8 KB/fragment; long sessions reach 150+ retained fragments). The repo shows the code path and lets you watch the parcel grow with depth; the crash-scale evidence is the production bundle above.Why this seems in scope for react-native-screens. RNS already declares its fragments non-restorable — it discards restored fragment state and rebuilds from JS (
AutoRemovingFragment, and the discussion in #96). But it still lets that state be saved into the Activity parcel, where on deep stacks it triggersTransactionTooLargeException. Since RNS never consumes the saved fragment state, arguably it should not let it balloon the save parcel — e.g. opt itsScreenStackFragments out of instance-state saving, or expose a supported way to bound it.App-side workaround (and a gotcha). Stripping the fragment state in
MainActivity.onSaveInstanceStatestops the crash — RNS rebuilds fine from JS. Note: on androidx.fragment 1.8.x theFragmentManagerstate is saved viaSavedStateRegistry, so it is nested — the classic top-leveloutState.remove("android:support:fragments")is a no-op (measured: 0 bytes removed). It must be reached inside the registry bundle:This depends on internal androidx key strings, which is why a first-party RNS option would be preferable.
Steps to reproduce
Reproduction repo: https://github.com/MisterMunchkin/rns-4487-repro
git clone https://github.com/MisterMunchkin/rns-4487-repro && cd rns-4487-repro && npm installnpx expo prebuild --platform android --cleanthennpx expo run:androidon an Android 13+ emulator/device. New Architecture is enabled viaapp.json; a parcel-size logger is injected intoMainActivitybyplugins/with-parcel-logger.js.adb logcat -s RNSRepro.TARGET_DEPTH. When it settles, enable Developer Options → "Don't keep activities" (forces the save+stop deterministically on an emulator; production hits it via natural memory pressure), then press Home.RNSReprologsonSaveInstanceState parcel=<N> bytes | regHasFragments=true— confirming the react-native-screens fragments accumulate in the Activity's saved state (nested underandroidx.lifecycle.BundlableSavedStateRegistry.key). RaiseTARGET_DEPTH(or run against content-heavy screens) to watch the parcel climb toward the Binder limit; at production scale (real screens, 150+ fragments ≈ 428 KB) it crosses ~639 KB and throwsTransactionTooLargeExceptionatactivityStopped.Snack or a link to a repository
https://github.com/MisterMunchkin/rns-4487-repro
Screens version
4.18.0
React Native version
0.81.5
Platforms
Android
JavaScript runtime
Hermes
Workflow
Expo bare workflow (
expo prebuild/ CNG; androidx.fragment 1.8.9)Device
Real device (production crash) + Android emulator (mechanism)
Device model
Samsung Galaxy A57 (SM-A576B), Android 16
Acknowledgements
Yes