
Revenuecat Troubleshoot
- 339 installs
- 55 repo stars
- Updated August 3, 2026
- revenuecat/ai-toolkit
Diagnose RevenueCat receipt validation failures, stale entitlements, sandbox mismatches, and webhook delays when live subscribers report billing or access issues.
About
Revenuecat-troubleshoot from revenuecat/ai-toolkit supports production debugging of RevenueCat integrations by tracing receipt errors, entitlement drift, sandbox configuration mistakes, and webhook delays so mobile teams can restore subscriber access and stabilize in-app purchase operations.
- Receipt and entitlement diagnostics
- Sandbox vs production pitfalls
- Webhook and sync delay checks
- Customer restore playbooks
- Faster billing incident resolution
Revenuecat Troubleshoot by the numbers
- 339 all-time installs (skills.sh)
- +38 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #118 of 596 Debugging skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/revenuecat/ai-toolkit --skill revenuecat-troubleshootAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 339 |
|---|---|
| repo stars | ★ 55 |
| Last updated | August 3, 2026 |
| Repository | revenuecat/ai-toolkit ↗ |
What it does
Diagnose RevenueCat receipt validation failures, stale entitlements, sandbox mismatches, and webhook delays when live subscribers report billing or access issues.
Files
revenuecat-troubleshoot: diagnose RevenueCat integration problems
Use this skill when the user reports a RevenueCat behavior that does not match expectations: empty offerings, missing products, an entitlement that does not unlock after a successful purchase, a paywall that fails to render, or sandbox transactions that never reach the dashboard.
This skill combines two angles:
1. Code-side diagnosis — turn on debug logging, walk a universal checklist, drop into platform specifics. 2. Dashboard inspection — use the RevenueCat MCP server to read the project, apps, products, entitlements, offerings, and webhooks, and offer fixes.
Work them in order. Most reports resolve before you reach the platform specifics.
1. Detect the platform
Inspect the working directory and pick the first match, from top to bottom:
1. React Native: package.json has a react-native-purchases entry, or react-native as a dependency → read platforms/react-native.md. If expo is also a dependency, note it as an Expo project. 2. Flutter: pubspec.yaml exists at the project root → read platforms/flutter.md. 3. Kotlin Multiplatform: build.gradle.kts contains a kotlin { … } multiplatform source sets block, or depends on com.revenuecat.purchases:purchases-kmp* → read platforms/kmp.md. 4. Android (native): build.gradle(.kts) applies com.android.application (and is not KMP) → read platforms/android.md. 5. iOS (native): Package.swift, *.xcodeproj, *.xcworkspace, or Podfile at the project root → read platforms/ios.md.
If several match (e.g. an ios/ folder inside a Flutter project), pick the outermost project, the one that owns the build. If still ambiguous, ask the user which platform the bug reproduces on.
2. Universal code-side checklist
Walk these nine items in order. Most reports are resolved by steps 1 through 5.
1. Turn on debug logging and reproduce. The SDK narrates what it is doing. Roughly 80% of reports are diagnosable from the log output alone. Each platform file shows how to set logLevel to debug. 2. Verify the API key platform matches the app. iOS apps must use an appl_… public SDK key. Android apps must use goog_… (or amzn_… for Amazon). A mismatched key produces an authentication error on the first network call. On iOS this surfaces as an INVALID_CREDENTIALS error code. On Android it surfaces as PurchasesErrorCode.InvalidCredentialsError. Use the list-app-public-api-keys RevenueCat MCP tool to list the API keys for the project. 3. Verify the bundle ID / package name matches the one set up in the RevenueCat project. List the apps using the list-apps RevenueCat MCP tool. The bundle_id (iOS / App Store) or package_name (Android / Play Store / Amazon Appstore) registered there must match the built app exactly, including capitalization. A mismatch causes offerings to come back empty because the app is not recognized. 4. Verify offerings in the RevenueCat project. List the project's offerings using the list-offerings RevenueCat MCP tool, passing the parameter expand=items.package.product. The offering marked with is_current: true must have at least one package attached, and each package must reference a store product. An offering with zero packages returns an empty availablePackages list even though getOfferings succeeds. 5. Verify store products are live. Products must be in "Ready to Submit" on App Store Connect or "Active" on Google Play Console. A product in a draft state will not be returned by the store, even in sandbox. If the SDK logs show offerings arriving from RevenueCat but products failing to resolve, this is almost always the cause. Use the get-product-store-state RevenueCat MCP tool to understand the state of product in App Store Connect or Google Play Console (field store_status). 6. Verify the testing account. iOS: the device must be signed into a Sandbox Apple ID under Settings → App Store → Sandbox Account (set on iOS 14+ after the first sandbox prompt). Android: the tester's Gmail must be added to Google Play Console → Setup → License testing, and the app must be installed via the Internal Testing opt-in link, not sideloaded. 7. Verify the network. Corporate VPNs, captive portals, and some DNS filters silently block the RevenueCat API or the store APIs. Try a different network before digging deeper. 8. Verify the appUserID. If logIn(appUserID) was called with an ID that does not match what the user expects, entitlements appear missing because they are attached to a different RC user. Print Purchases.shared.appUserID (iOS) / Purchases.sharedInstance.appUserID (Android) and confirm it matches. 9. Reset and retry. Uninstall the app, re-sign into the sandbox / tester account, reinstall from the correct channel, relaunch.
3. Dashboard inspection via the RevenueCat MCP
Use this when steps 3, 4, or 5 above point at dashboard configuration, when the user has no working app yet, or when you need to confirm a fix landed.
Important: The API key may have access to multiple projects. Always call list-projects first. If multiple projects are returned, ask the user which to inspect.
Phase A: gather context
1. Symptom — "What specifically isn't working? What error messages are you seeing? Which platform (iOS, Android, Web)?" 2. User state — "Is this happening for new purchases or existing subscribers? Sandbox or production?"
Phase B: systematic diagnosis
Work through this checklist via MCP tools:
Check 1: Project overview
list-projects → ask user to select project if multiple
list-apps (with selected project_id)- Verify project exists and apps are present.
Check 2: Products
list-products
get-product-store-state- [ ] Products exist for each store item.
- [ ] Store identifiers match App Store Connect / Play Console exactly.
- [ ] Product types are correct (subscription vs one-time).
- [ ] Play Store: using
product_id:base_plan_idformat. - [ ] Store State:
store_status.status=ok.
Check 3: Entitlements
list-entitlements
get-products-from-entitlement (for each entitlement)- [ ] Entitlements exist for each access level.
- [ ] Products are attached to entitlements.
- [ ] No orphaned products (products not granting any entitlement).
Check 4: Offerings
list-offerings
list-packages- [ ] At least one offering exists with
is_current: true. - [ ] Packages contain products.
- [ ] Package identifiers use standard conventions (
$rc_monthly, etc.).
Check 5: Webhooks (if server-side issues suspected)
list-webhook-integrations- [ ] Webhook URL is correct and accessible.
- [ ] Environment matches (production vs sandbox).
Phase C: report and offer fixes
Diagnostic Report
=================
Project: {project_name}
Checks Passed: ✅
- Project exists and is accessible
- 2 apps configured (iOS, Android)
- 4 products found
Issues Found: ⚠️
1. CRITICAL: Product not attached to entitlement
Product: annual_premium (prod123)
Fix: Attach this product to an entitlement
2. WARNING: Offering has empty package
Offering: default / Package: $rc_annual has no products
Fix: Attach annual_premium to this package
3. INFO: No webhook configured
Optional but recommended for server-side access control
Recommended Actions:
1. Attach annual_premium to "premium" entitlement
2. Attach annual_premium to $rc_annual package
Would you like me to fix issues #1 and #2 now?For each fixable issue, confirm with the user, then execute via MCP:
attach-products-to-entitlementattach-products-to-package
4. Platform specific step
Read the platform file that matches detection. Each one lists platform specific gotchas not covered above (StoreKit configuration files, Gradle/desugaring, Metro caching, Expo prebuild, etc.).
platforms/ios.mdplatforms/android.mdplatforms/kmp.mdplatforms/flutter.mdplatforms/react-native.md
5. Verify the fix
Do not declare the issue fixed until:
1. The log that previously showed the error now shows the expected success line (offerings returned with at least one package, purchase completed, entitlement active). 2. The dashboard reflects the change. For a purchase, check the Sandbox view on the Customers page and confirm the transaction is attached to the right appUserID. 3. The reproduction steps from the original report now pass.
If the user cannot reproduce locally, have them send the full debug log from app launch to the moment of failure. The SDK's own output is usually enough.
---
Reference: SDK error codes
Common errors
| Error code | Likely cause | Solution |
|---|---|---|
INVALID_APP_USER_ID | Reserved characters or empty string | Use alphanumeric IDs, underscores, hyphens only |
INVALID_CREDENTIALS | Wrong API key or bundle ID mismatch | Verify API key matches app |
NETWORK_ERROR | No connectivity or firewall | Check network, verify RevenueCat domains allowed |
STORE_PROBLEM | Store downtime, config issue, iOS 18.x bug | Check store status, verify config, see Known iOS Issues below |
SIGNATURE_VERIFICATION_FAILED | Tampered receipt or config error | Verify In-App Purchase Key (iOS) or service credentials |
Purchase errors
| Error code | Solution |
|---|---|
RECEIPT_ALREADY_IN_USE | Call restorePurchases() or sync customer |
PRODUCT_NOT_AVAILABLE_FOR_PURCHASE | Verify product status in App Store Connect / Play Console |
PURCHASE_NOT_ALLOWED | Check parental controls, payment method |
PRODUCT_ALREADY_PURCHASED | Call restorePurchases() to sync |
Reference: debug log interpretation
Ask the developer to enable debug logging:
- iOS:
Purchases.logLevel = .debug - Android:
Purchases.logLevel = LogLevel.DEBUG
Log emoji indicators: 🍎 Apple/StoreKit · 🤖 Google Play · 📦 Amazon · 😿 RevenueCat backend.
Reference: known platform issues
iOS
iOS 18.0–18.3.2: StoreKit Daemon Connection Failure
- Symptom:
STORE_PROBLEM(NSCocoaErrorDomain Code 4097) on ~25% of purchases on physical devices. - Fix: Upgrade to iOS 18.4+.
iOS 18.4–18.5 Simulator: Products Don't Load
- Symptom: Products return empty in simulator with sandbox.
- Affected: Simulator only — physical devices and production unaffected.
- Fix: Test on physical device, or use Xcode 26+ with iOS 26+ simulators.
Android
ProxyBillingActivity NullPointerException
- Typically from automated testing or Play Store pre-launch reports on LG Nexus 5X / rooted devices.
- Safe to ignore/silence in crash reporting tools.
NoCoreLibraryDesugaringException / NoClassDefFoundError
- Fix: Enable core library desugaring in
build.gradleor raiseminSdk.
Reference: platform configuration checklists
iOS
- [ ] Paid Applications agreement signed in App Store Connect.
- [ ] In-App Purchase Key uploaded to RevenueCat (StoreKit 2 / SDK 5.x+).
- [ ] Products show "Ready to Submit" or "Approved" status.
- [ ] Bundle ID matches exactly in Xcode, App Store Connect, and RevenueCat.
- [ ] New products: wait 24h for propagation.
Android
- [ ] App published to at least closed testing track (internal testing won't work).
- [ ] Test account added as licensed tester in Play Console.
- [ ] Service account credentials (JSON) uploaded to RevenueCat with Finance permissions.
- [ ] Subscriptions use
product_id:base_plan_idformat. - [ ] New products: wait 24h for propagation.
Reference: App Store rejection troubleshooting
"Issues fetching products" — Products must be submitted for review with the app on first submission. Create products in App Store Connect, then submit app and products together.
"Error during purchase" (Sandbox) — Apple sandbox downtime. Inform reviewer, provide RevenueCat sandbox dashboard screenshot showing test purchases work, ask to retry.
"Content not unlocked after purchase" — Verify product → entitlement connection in RevenueCat. Ensure app calls getCustomerInfo() after purchase.
Reference: common issues
User purchased but has no entitlement — Check product → entitlement attachment and verify store identifier matches exactly.
Offering returns empty — Verify a current offering exists, packages have products attached, and products exist in the app's store.
Webhook not receiving events — Verify URL is internet-accessible and returns 200 OK. Test with webhook.site.
Subscription status out of sync — SDK caches CustomerInfo for 5 min (foreground). Force refresh:
// iOS
Purchases.shared.getCustomerInfo(fetchPolicy: .fetchCurrent) { ... }// Android
Purchases.sharedInstance.getCustomerInfoWith(CacheFetchPolicy.FETCH_CURRENT) { ... }SDK crashes on launch (iOS / Xcode 26) — Initialize RevenueCat before other networking libraries.
SDK crashes on launch (Android) — Enable core library desugaring or raise minSdk to 24+.
revenuecat-troubleshoot: Android (native Kotlin/Java)
Work the universal checklist in ../SKILL.md first. This file covers issues that only show up on Android.
Turn on debug logging
import com.revenuecat.purchases.LogLevel
import com.revenuecat.purchases.Purchases
Purchases.logLevel = LogLevel.DEBUGSet this before Purchases.configure(...) in your Application.onCreate(). Filter logcat by the Purchases tag:
adb logcat -s PurchasesExpected configure banner:
Purchases: ℹ️ [Purchases] - INFO: 😻👼 Purchases is configuredIf you see no Purchases tag output at all, android:name=".MyApplication" is missing from AndroidManifest.xml and the Application subclass never runs.
License testers and the Internal Testing track
This is the single most common Android gotcha.
To make a test purchase on Android you need all of the following:
1. The tester's Gmail is added under Google Play Console → Setup → License testing. 2. The app has been uploaded as a signed AAB to an Internal Testing track. 3. The tester has opted in via the Internal Testing opt-in link and installed the app from the Play Store (not via adb install or Android Studio direct install). Sideloaded builds with the same package name do not have access to licensed products. 4. The SHA-1 certificate fingerprint of the signing key used to build the AAB matches what Play Console expects for that track. Play App Signing changes the effective key; check the upload key and app signing key in Play Console → Setup → App signing. 5. Fresh uploads take roughly 15 minutes to propagate through Play. If you just uploaded, wait before retesting.
Sideloaded debug builds with a debug signing key cannot buy real or sandbox Play products. This is a Play Billing constraint, not a RevenueCat one.
Products return empty list
If Purchases.sharedInstance.getOfferings(...) returns an Offerings with no available packages, or offerings come back but products are missing:
- Check logcat for
BillingClienterrors.BillingResponseCode.BILLING_UNAVAILABLEusually means the Play Store app is out of date or the user is not signed in. ITEM_UNAVAILABLEmeans the product ID in the RevenueCat dashboard does not match a live product in Play Console, or the product is in draft state.- Product IDs in Play Console are case sensitive and cannot be reused once deleted.
- New subscriptions require at least one active base plan. A subscription with no base plan is not available for purchase.
applicationId mismatch
The applicationId in android/app/build.gradle(.kts) (not the Kotlin package) must match the app registered in the RevenueCat dashboard. A mismatch causes every request to return "app not found" equivalents. Check:
// app/build.gradle.kts
android {
defaultConfig {
applicationId = "com.example.myapp"
}
}against Dashboard → Project → Apps → Android → Package name.
Flavor builds can shift the applicationId (e.g. com.example.myapp.dev). Each flavor needs its own entry in the dashboard or its own RevenueCat project.
Entitlement not active after purchase
- Log
Purchases.sharedInstance.getCustomerInfo(...)right after the purchase callback fires. The freshCustomerInfois returned there. - Confirm the product is attached to an entitlement in the dashboard.
- If
PurchasesAreCompletedBy.MY_APPis configured, the SDK does not acknowledge the purchase. Your own code must callBillingClient.acknowledgePurchase(...)within 3 days or Play will refund the charge automatically. This is a Play Billing rule.
Proguard / R8
The SDK ships consumer proguard rules, so no extra configuration is needed in release builds. If you see reflection related crashes only in release, confirm minifyEnabled true is paired with shrinkResources true and that you have not overridden the SDK's consumer rules.
Verify
After the fix, reproduce the scenario with LogLevel.DEBUG and confirm logcat shows the success path. Confirm the transaction appears on the RevenueCat dashboard Sandbox view with the correct appUserID. Drop log level back to INFO or WARN before shipping.
revenuecat-troubleshoot: Flutter
Work the universal checklist in ../SKILL.md first. Most Flutter reports reproduce against the underlying native SDK, so the platform files in platforms/ios.md and platforms/android.md apply.
Turn on debug logging
import 'package:purchases_flutter/purchases_flutter.dart';
await Purchases.setLogLevel(LogLevel.debug);Do this before Purchases.configure(...). The native SDK banner appears on the platform console (Xcode for iOS, logcat for Android), not the Flutter console. To see it:
- iOS → Xcode → Window → Devices and Simulators → pick device → Open Console, or run
flutter runwhile Xcode is attached. - Android →
flutter logsoradb logcat -s Purchases.
If you only see Dart print output and no native SDK logs, the platform consoles are not attached. That is the most frequent "I have no logs" situation on Flutter.
Clean before blaming the SDK
After any change to pubspec.yaml, the iOS Podfile, or a native dependency, run:
flutter clean
flutter pub get
cd ios && pod install && cd ..Hot restart does not re-run native initialization. After changing the API key or Purchases.configure call, do a full stop and relaunch. Hot reload / hot restart keep the old SDK state.
Platform branching errors
import 'dart:io';
final apiKey = Platform.isIOS
? 'appl_YOUR_IOS_PUBLIC_SDK_KEY'
: 'goog_YOUR_ANDROID_PUBLIC_SDK_KEY';A common report: the Android build works but iOS shows "invalid credentials". Cause: Platform.isIOS returned false because the check ran on a macOS desktop target, not iOS. purchases_flutter does not support macOS, Windows, Linux, or Web, so always confirm the test is running on an iOS or Android device.
Offerings come back empty
await Purchases.getOfferings()throws on network failure. Wrap in try/catch and print the exception.- An
Offeringsobject wherecurrent == nullmeans the dashboard has no current offering assigned. Fix in the dashboard. - An
Offeringwith an emptyavailablePackageslist means packages exist but none of their store products resolved. Check the native console for product lookup failures. This is almost always a product ID mismatch between RevenueCat and the store.
Paywall does not render
purchases_ui_flutter paywalls require:
- An offering with a paywall template configured in the dashboard.
- Minimum iOS 15 for the iOS renderer. Android minSdk 24 for the Android renderer. Check your deployment targets.
- A fresh offerings fetch after the dashboard was configured. Cached stale offerings without the paywall will render nothing.
Entitlement not active after purchase
await Purchases.getCustomerInfo()right after a purchase returns the fresh state. Do not rely on a cached variable.- The product must be attached to an entitlement in the dashboard. A product with no entitlement attachment succeeds as a purchase but flips no flag.
- For multi flavor apps, each flavor has its own
applicationIdand needs its own dashboard entry.
Verify
Reproduce with debug logging on, watching the native platform console (not the Dart console). Confirm the success log, then confirm the dashboard reflects the transaction. Drop log level before the next release:
await Purchases.setLogLevel(LogLevel.info);revenuecat-troubleshoot: iOS (native)
Work the universal checklist in ../SKILL.md first. This file covers issues that only show up on iOS.
Turn on debug logging
import RevenueCat
Purchases.logLevel = .debugSet this before Purchases.configure(...). The SDK emits a banner when configuration succeeds:
[Purchases] - INFO: 😻👼 Purchases is configuredIf you do not see that line at app launch, configure is not running. Check that the call is in the SwiftUI App.init or the UIApplicationDelegate.application(_:didFinishLaunchingWithOptions:) entry point.
StoreKit Configuration File vs real sandbox
This is the single most common iOS gotcha.
If a StoreKit Configuration File is attached to the active scheme (Xcode → Edit Scheme → Run → Options → StoreKit Configuration), the app runs against Xcode's synthetic local store. Transactions there do not flow through Apple's sandbox and do not hit the RevenueCat backend the same way a real sandbox purchase does. Specifically:
- Purchases will appear to succeed in the app.
- The RevenueCat dashboard will not show the transaction in the Sandbox view.
- Entitlements may or may not update depending on SDK version and StoreKit version.
To test against the real sandbox, detach the StoreKit configuration from the scheme, then run on a device signed into a Sandbox Apple ID. Settings → App Store → Sandbox Account holds the tester login on iOS 14+.
The StoreKit config file is still useful for pure UI iteration, but any bug report that involves "purchase does not appear on dashboard" must be reproduced without it.
Sandbox tester setup
1. App Store Connect → Users and Access → Sandbox → Testers → create a tester with an email that is not associated with a real Apple ID. 2. On the device, sign out of the production App Store account is not required in iOS 14+. Instead, Settings → App Store → Sandbox Account holds a separate sandbox login. 3. Trigger a purchase in the app. iOS will prompt for the sandbox password on the first purchase. 4. Accelerated renewal: monthly subscriptions renew every 5 minutes in sandbox, weekly renews every 3 minutes, yearly every 1 hour. A subscription auto-renews up to 6 times then expires.
TestFlight builds behave like production for receipt purposes. Transactions in TestFlight appear in the production dashboard view, not the Sandbox view.
Offerings come back empty
If Purchases.shared.offerings() returns an Offerings object with current == nil or current?.availablePackages.isEmpty == true:
- Check the log for
Error fetching offerings. If present, the dashboard is misconfigured or the bundle ID does not match. - If the log shows offerings fetched but packages empty, the "current" offering in the dashboard has no packages attached.
- If the log shows products failing to be fetched from StoreKit, the product IDs in the RevenueCat dashboard do not match what App Store Connect has approved. Product IDs are case sensitive.
Paywall does not render
For RevenueCatUI paywalls:
- A paywall template must be configured on the offering in the dashboard. An offering without a paywall renders nothing.
- The paywall view requires a valid
Offeringinstance. If you fetched a stale cached offerings object before the dashboard was configured, restart the app to force a refetch. - On iOS 13, the paywall UI is not available.
RevenueCatUItargets iOS 15+. Check your deployment target.
Entitlement not active after a purchase
- Log
Purchases.shared.customerInfo()right afterpurchase(...)returns. The returnedCustomerInfocarries the fresh state, not the cached one. - Confirm the product's entitlement attachment in the dashboard: Product → Attach to entitlement. A product that is not attached to any entitlement will succeed as a purchase but not flip any entitlement.
- If
purchasesAreCompletedByis set to.myApp, RevenueCat does not finish the transaction. Your own StoreKit code must complete it, and the SDK only observes. If both the SDK and your code try to finish the transaction, entitlement state can appear inconsistent.
Verify
After the fix, reproduce the original scenario with Purchases.logLevel = .debug and confirm the log shows the success path, then confirm the RevenueCat dashboard reflects the new transaction or entitlement state. Remove the debug log level before shipping the next release.
revenuecat-troubleshoot: Kotlin Multiplatform
purchases-kmp wraps the native iOS and Android SDKs. Bugs almost always reproduce on one specific target, and the fix is the same as the native SDK fix for that target.
Identify which target fails first
Run the app on each target and reproduce the bug. Note whether it happens on:
- iOS only → the underlying issue is in
purchases-ios. Readios.md. - Android only → the underlying issue is in
purchases-android. Readandroid.md. - Both → the issue is configuration shared across targets, usually the API key, appUserID, or offerings setup.
Turn on debug logging
In the shared module:
import com.revenuecat.purchases.kmp.LogLevel
import com.revenuecat.purchases.kmp.Purchases
Purchases.logLevel = LogLevel.DEBUGThis flows to both native SDKs. The log output on each target matches the native SDK output, so follow the native platform file for the expected log lines.
API key confusion
The KMP setup passes a different API key per platform. A very common bug: the shared code hardcodes the iOS key on both platforms (or vice versa). Symptom: works on one platform, fails with InvalidCredentialsError on the other.
Confirm the platform specific entry points pass the matching key:
- Android
Application.onCreate()→goog_… - iOS
@main App.init()→appl_…
Version mismatch between KMP and native SDKs
purchases-kmp-core tracks native SDK versions but does not always update in lockstep. If the KMP version is newer than the native CocoaPods or Maven artifact it bridges to, you can hit missing symbol errors at link time or runtime.
Check the installed purchases-kmp-core version in your build.gradle.kts and cross reference the native versions it depends on. If in doubt, pin the native iOS and Android SDKs explicitly to known good versions.
iOS framework linking issues
If the Kotlin framework builds but the iOS app fails to link RevenueCat symbols, the CocoaPods integration may not be pulling the bridge pod. Rebuild with:
cd iosApp
pod deintegrate
pod installThen clean the Xcode build folder (Product → Clean Build Folder) before running again.
Android Context missing
If the Android actual of PurchasesConfiguration requires a Context and the shared code does not supply one, the app crashes at configure time. Construct the configuration on the Android side where this (the Application) is in scope, then call into shared initialization for anything that does not need Context.
Verify
Reproduce on the target that was failing. Confirm the log banner and the success log for the scenario. Since purchases-kmp delegates to the native SDK, the RevenueCat dashboard should reflect the transaction exactly as it would for a native app.
revenuecat-troubleshoot: React Native
Work the universal checklist in ../SKILL.md first. React Native bugs usually surface in JavaScript but are rooted in the underlying native SDK. The iOS and Android platform files in platforms/ apply.
Turn on debug logging
import Purchases, { LOG_LEVEL } from 'react-native-purchases';
Purchases.setLogLevel(LOG_LEVEL.DEBUG);Call this before Purchases.configure(...). The SDK logs on the native console, not the Metro bundler console:
- iOS → Xcode console when the app is attached, or Console.app filtering by the app's bundle ID.
- Android → Android Studio Logcat, or
adb logcat -s Purchases.
If you only see JS output in Metro, you are not looking at the SDK logs. This catches most "I have no logs" reports.
Expo Go vs dev client
react-native-purchases links native code and will throw if run in Expo Go. Symptom: any call to Purchases.configure or Purchases.getOfferings rejects with a "native module not found" error.
Fix: produce a development build.
npx expo prebuild # bare workflow
# or
eas build --profile developmentInstall the dev client, then reload. Expo Go itself cannot be fixed to support this.
Cache problems after a dependency change
After changing react-native-purchases, the iOS Podfile, or Android Gradle files, clear Metro and rebuild natively:
npx react-native start --reset-cache
cd ios && pod install && cd ..
# or for Expo:
npx expo prebuild --cleanStale Metro caches can bundle the old JS against a new native module, producing confusing mismatches.
Platform branching errors
import { Platform } from 'react-native';
const apiKey = Platform.OS === 'ios'
? 'appl_YOUR_IOS_PUBLIC_SDK_KEY'
: 'goog_YOUR_ANDROID_PUBLIC_SDK_KEY';A common report: one platform works, the other returns INVALID_CREDENTIALS. Cause: the Platform.OS check fell through to the wrong branch, or both platforms share a single key constant.
Offerings empty or products missing
- Log the output of
await Purchases.getOfferings(). If the returned object hascurrent: nullorcurrent.availablePackagesis an empty array, the dashboard offering is misconfigured. - Watch the native console for product lookup failures. Product IDs in the dashboard must match the store exactly, including case.
Paywall does not render
react-native-purchases-ui:
- The offering must have a paywall configured in the dashboard.
- iOS renderer requires iOS 15. Android renderer requires minSdk 24. Check your project's deployment targets.
- If you fetched offerings before the dashboard paywall was configured, restart the app to bust the cache.
Entitlement not active after purchase
- Await
Purchases.getCustomerInfo()after the purchase promise resolves to get the fresh state. - The product must be attached to an entitlement in the dashboard.
- On Android, if your app is sideloaded instead of installed from Play Internal Testing, purchases will fail or behave inconsistently. Use
eas build --profile previewor a signed AAB installed via the Play opt-in link.
iOS pod install forgotten
After bumping react-native-purchases, cd ios && pod install is required. Without it, the native code on iOS is still the old version, and strange crashes at configure time appear. Check ios/Podfile.lock to confirm the installed pod version matches the JS package version.
Verify
Reproduce with LOG_LEVEL.DEBUG and watch the native platform console. Confirm the success log, then confirm the RevenueCat dashboard shows the transaction. Drop back to LOG_LEVEL.INFO before shipping.