RevenueCat / RevenueCat/react-native-purchases

OutOfMemoryError when displaying paywall with social proof carousel

Open
#1,492 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
TypeScript
Stars
1.2k
Forks
117
Avg merge
14h 40m
Merged PRs (30d)
40

Description

Describe the bug

The RevenueCat native Compose paywall UI (react-native-purchases-ui) causes consistent OutOfMemoryError crashes on Android when displaying paywall with social proof carousel. The paywall exhausts the 256MB heap limit during its initial render, even with a simplified template containing no custom images or complex UI elements.

1. Environment

  1. Platform: Android
  2. SDK version:
    • react-native-purchases: 9.6.6
    • react-native-purchases-ui: 9.6.6
  3. OS version: Android 16
  4. Android Studio version: N/A (using Expo managed workflow)
  5. React Native version: 0.79.6
  6. SDK installation: npm/yarn with Expo managed workflow
  7. How widespread is the issue: 100% reproducible on test devices when displaying hard paywall via RevenueCatUI.Paywall

2. Debug logs

The crash occurs during the initial Compose UI measurement/layout phase:

2025-11-18 14:17:38.341 21374-21374 com.example.app         com.example.app              W  Throwing OutOfMemoryError "Failed to allocate a 4224 byte allocation with 2036688 free bytes and 1988KB until OOM, target footprint 268435456, growth limit 268435456; giving up on allocation because <1% of heap free after GC."

2025-11-18 14:17:38.347 21374-21374 AndroidRuntime          com.example.app              E  FATAL EXCEPTION: main
Process: com.example.app, PID: 21374
java.lang.OutOfMemoryError: Failed to allocate a 4224 byte allocation with 2036688 free bytes and 1988KB until OOM, target footprint 268435456, growth limit 268435456; giving up on allocation because <1% of heap free after GC.
	at android.os.Parcel.nativeReadString8(Native Method)
	at android.os.Parcel.readString8NoHelper(Parcel.java:3436)
	at android.os.Parcel$ReadWriteHelper.readString8(Parcel.java:545)
	at android.os.Parcel.readString8(Parcel.java:3415)
	at android.view.DisplayShape$1.createFromParcel(DisplayShape.java:261)
	at android.view.InsetsState.readFromParcel(InsetsState.java:852)
	at android.window.WindowMetricsController.getWindowInsetsFromServerForDisplay(WindowMetricsController.java:117)
	at androidx.compose.material3.adaptive.AndroidWindowAdaptiveInfo_androidKt.currentWindowSize(AndroidWindowAdaptiveInfo.android.kt:58)
	at androidx.compose.material3.adaptive.AndroidWindowAdaptiveInfo_androidKt.currentWindowAdaptiveInfo(AndroidWindowAdaptiveInfo.android.kt:37)
	at com.revenuecat.purchases.ui.revenuecatui.components.iconcomponent.IconComponentStateKt.rememberUpdatedIconComponentState(IconComponentState.kt:50)
	at com.revenuecat.purchases.ui.revenuecatui.components.iconcomponent.IconComponentViewKt.IconComponentView(IconComponentView.kt:47)
	at com.revenuecat.purchases.ui.revenuecatui.components.ComponentViewKt.ComponentView(ComponentView.kt:81)
	at com.revenuecat.purchases.ui.revenuecatui.components.stack.StackComponentViewKt$MainStackComponent$stack$1$2$1$1.invoke(StackComponentView.kt:513)
	[... extensive Compose layout stack trace ...]
	at com.revenuecat.purchases.react.ui.views.ComposeViewWrapper.onMeasure(ComposeViewWrapper.kt:52)

Key observations from logs:

  • Memory pressure builds up during onboarding: Clamp target GC heap from 351MB to 256MB
  • Multiple blocking GC events before crash: WaitForGcToComplete blocked Alloc (400-600ms delays)
  • Heap is at 254-255MB before paywall renders
  • Crash occurs during Compose UI measurement, specifically when calculating adaptive window metrics

3. Steps to reproduce

Setup:

  1. Create a React Native app with Expo managed workflow
  2. Add RevenueCat paywall using react-native-purchases-ui at the end of onboarding
  3. Use the default offering

Expected behavior:
Paywall should render successfully without memory issues.

Actual behavior:
App crashes with OutOfMemoryError during paywall's initial Compose measurement phase. The crash is 100% reproducible.

Additional observations:

  • Removing all WebP images from onboarding slides does NOT fix the issue
  • Removing all custom images from the paywall template does NOT fix the issue
  • Memory is already at 254-255MB before the paywall attempts to render
  • Adding android:largeHeap="true" does NOT fix the issue (still crashes at 256MB limit)

4. Other information

Root cause analysis:

The RevenueCat Compose paywall UI appears to have excessive memory overhead when rendering paywalls with social proof carousel. The stack trace shows it's crashing during:

  1. Adaptive window metrics calculation (currentWindowAdaptiveInfo)
  2. Icon component state management
  3. Complex nested Stack/Carousel component composition

The issue appears to be related to:

  1. Jetpack Compose memory overhead - The native Compose UI requires significantly more memory than React Native views
  2. Complex component hierarchy - Multiple nested stacks, carousels, and adaptive layouts
  3. No memory cleanup between screens - Previous onboarding state remains in memory

Workarounds attempted:

  • ✅ Removed all images from onboarding slides
  • ✅ Simplified paywall template (removed all custom images)
  • ✅ Added android:largeHeap="true"
  • ❌ None of these resolved the issue

Removing the carousel:

  • ✅ This seems to have fixed the crash

Device specifications:

  • Heap limit: 256MB (standard Android allocation)
  • Memory available at crash: ~2MB (254MB/256MB used)
  • React Native memory footprint before paywall: ~200MB

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the crash by displaying RevenueCatUI.Paywall with the social proof carousel in the described Expo setup. Start with ComposeViewWrapper.kt, IconComponentState.kt, and the stack/carousel components named in the trace, comparing behavior with the carousel removed. Done means the paywall renders without an OutOfMemoryError under the reported conditions.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, react-native
Domain
mobile-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.