RevenueCat Network Error / Connection Failed

8:00

What This Error Means

The NETWORK_ERROR (sometimes shown as "network connection lost" or "request timed out") means the RevenueCat SDK could not complete an HTTP request to the RevenueCat API servers, or the underlying App Store / Google Play Billing request failed due to connectivity. Unlike configuration errors, network errors are almost always transient — they reflect a temporary connectivity issue rather than a bug in your code.

Network errors can surface when the user is offline, when RevenueCat's servers experience an incident, or when something in the network path (ATS, VPN, firewall) is blocking the connection. The right response is to catch the error, inform the user, and offer a retry path — not to crash or permanently block the paywall.

Key point: RevenueCat caches customer info and entitlements locally. Even when network errors occur, previously fetched entitlement data remains accessible via the cache. Always check the cached customer info before assuming the user has no active subscription.

Error Messages

text
# RevenueCat SDK (iOS and Android)
PurchasesError(code=NETWORK_ERROR, message="Network error")

# iOS URLSession (underlying transport)
Error: The network connection was lost. (NSURLErrorDomain error -1005)
Error: The request timed out. (NSURLErrorDomain error -1001)
Error: A server with the specified hostname could not be found. (NSURLErrorDomain error -1003)

# Android OkHttp / Retrofit (underlying transport)
java.net.SocketTimeoutException: timeout
java.net.UnknownHostException: Unable to resolve host "api.revenuecat.com"
java.io.IOException: Network is unreachable

# Flutter / React Native
PlatformException(NETWORK_ERROR, Network error, null, null)
RevenueCatError: NETWORK_ERROR (9) - Network error

Common Causes

  1. Device is offline or on a poor connection

    The most common cause by far. Cellular handoffs, entering a tunnel, or switching Wi-Fi networks can temporarily drop connectivity. RevenueCat API calls require an active internet connection to validate receipts and update entitlements.

  2. RevenueCat API servers temporarily unreachable

    Like all cloud services, RevenueCat occasionally experiences brief incidents. Check status.revenuecat.com during widespread or persistent network errors to see if there's an active incident. Most incidents resolve within minutes.

  3. App Transport Security (ATS) blocking requests on iOS

    iOS's ATS enforces strict HTTPS requirements. RevenueCat's API uses HTTPS exclusively, so it should not be blocked by default. However, if you've added custom NSAppTransportSecurity entries in your Info.plist that restrict all domains (e.g., NSAllowsArbitraryLoads: NO combined with a domain allowlist that doesn't include RevenueCat), you can inadvertently block the SDK.

  4. VPN or firewall blocking connections

    Corporate VPNs, content filtering firewalls, or parental control software can block outbound HTTPS to RevenueCat's domains. This is most common in enterprise or education environments. Test on a different network (e.g., cellular data) to isolate this cause.

  5. Emulator/Simulator has no network access

    Android emulators and iOS simulators can sometimes lose their network connection after a host machine sleeps or changes networks. Restart the emulator or simulator and verify internet access by opening a browser inside it.

How to Handle Network Errors Gracefully

Network errors are expected in production apps. Your paywall and purchase flow should handle them without crashing or giving users a confusing experience. The following patterns demonstrate proper error handling with retry logic:

Swift — Error handling with retry

swift
// Swift — Graceful network error handling
import RevenueCat

class PaywallViewModel: ObservableObject {
    @Published var errorMessage: String?
    @Published var isRetryable: Bool = false

    func loadOfferings() {
        Purchases.shared.getOfferings { [weak self] offerings, error in
            DispatchQueue.main.async {
                if let error = error as? RevenueCat.ErrorCode {
                    switch error {
                    case .networkError:
                        // Transient — offer retry
                        self?.errorMessage = "Connection failed. Please check your internet connection and try again."
                        self?.isRetryable = true

                    case .offlineConnectionError:
                        self?.errorMessage = "You appear to be offline. Connect to the internet to view subscription options."
                        self?.isRetryable = true

                    default:
                        // Non-network error — don't suggest retry
                        self?.errorMessage = "Something went wrong. Please restart the app."
                        self?.isRetryable = false
                    }
                }
            }
        }
    }

    func purchase(_ package: Package) {
        Purchases.shared.purchase(package: package) { transaction, customerInfo, error, userCancelled in
            DispatchQueue.main.async {
                if userCancelled { return }

                if let error = error as? RevenueCat.ErrorCode, error == .networkError {
                    // Purchase may have gone through — always restore to check
                    self.restorePurchasesAfterNetworkError()
                    return
                }

                // Handle success or other errors...
            }
        }
    }

    private func restorePurchasesAfterNetworkError() {
        // If the purchase call fails with a network error, the purchase may
        // have been processed by the App Store but not confirmed by RevenueCat.
        // Restoring purchases reconciles this state.
        Purchases.shared.restorePurchases { customerInfo, error in
            DispatchQueue.main.async {
                if let activeEntitlement = customerInfo?.entitlements.active.first {
                    print("Entitlement restored: \(activeEntitlement.key)")
                } else {
                    self.errorMessage = "Could not verify purchase. Please try again when connected."
                    self.isRetryable = true
                }
            }
        }
    }
}

Kotlin — Error handling with retry

kotlin
// Kotlin — Graceful network error handling
import com.revenuecat.purchases.*

class PaywallViewModel : ViewModel() {

    private val _errorMessage = MutableLiveData<String?>()
    val errorMessage: LiveData<String?> = _errorMessage

    private val _isRetryable = MutableLiveData(false)
    val isRetryable: LiveData<Boolean> = _isRetryable

    fun loadOfferings() {
        Purchases.sharedInstance.getOfferingsWithCompletion(
            onError = { error ->
                when (error.code) {
                    PurchasesErrorCode.NetworkError -> {
                        _errorMessage.postValue(
                            "Connection failed. Check your internet connection and try again."
                        )
                        _isRetryable.postValue(true)
                    }
                    else -> {
                        _errorMessage.postValue("Something went wrong. Please restart the app.")
                        _isRetryable.postValue(false)
                    }
                }
            },
            onSuccess = { offerings ->
                // Handle successful offerings load
            }
        )
    }

    fun purchase(activity: Activity, packageToPurchase: Package) {
        Purchases.sharedInstance.purchaseWith(
            PurchaseParams.Builder(activity, packageToPurchase).build(),
            onError = { error, userCancelled ->
                if (userCancelled) return@purchaseWith

                if (error.code == PurchasesErrorCode.NetworkError) {
                    // Restore purchases to reconcile potential App Store success
                    restorePurchasesAfterNetworkError()
                }
            },
            onSuccess = { storeTransaction, customerInfo ->
                // Handle successful purchase
            }
        )
    }

    private fun restorePurchasesAfterNetworkError() {
        Purchases.sharedInstance.restorePurchasesWith(
            onError = { error ->
                _errorMessage.postValue("Could not verify purchase. Please retry when connected.")
                _isRetryable.postValue(true)
            },
            onSuccess = { customerInfo ->
                val activeEntitlements = customerInfo.entitlements.active
                if (activeEntitlements.isNotEmpty()) {
                    Log.d("RevenueCat", "Purchase restored: ${activeEntitlements.keys}")
                } else {
                    _errorMessage.postValue("No active purchases found. Please retry.")
                    _isRetryable.postValue(true)
                }
            }
        )
    }
}
Purchase + network error: When a purchase call fails with NETWORK_ERROR, the charge may or may not have been processed by the App Store/Google Play. Always call restorePurchases() after a network error during checkout to ensure the customer receives their entitlement if the purchase did go through.

Debugging Steps

If users are consistently reporting network errors (not just transient ones), work through these steps to identify the root cause:

1. Verify device connectivity

Confirm the device has internet access before calling RevenueCat APIs. Use the system's network reachability APIs to gate purchases behind a connectivity check:

swift
// Swift — Quick connectivity check using NWPathMonitor
import Network

let monitor = NWPathMonitor()
monitor.pathUpdateHandler = { path in
    if path.status == .satisfied {
        print("Connected — safe to call RevenueCat APIs")
    } else {
        print("Not connected — show offline message to user")
    }
}
monitor.start(queue: DispatchQueue.global())

2. Check the RevenueCat status page

Visit status.revenuecat.com to check for active incidents. Subscribe to incident notifications to be alerted of future service disruptions.

3. Test on different networks

If you only see the error on one specific network (e.g., corporate Wi-Fi but not cellular), this points to a firewall or proxy issue. Test on cellular data, a home network, and a hotspot to isolate network-specific problems.

4. Check ATS settings in Info.plist (iOS)

xml
<!-- Info.plist — Do NOT add NSAllowsArbitraryLoads
     RevenueCat uses HTTPS and does not need ATS exceptions.
     If you have a domain allowlist, ensure it does not accidentally
     block api.revenuecat.com -->

<!-- CORRECT: No ATS overrides needed for RevenueCat -->

<!-- INCORRECT: This would block all domains not listed, including RevenueCat -->
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <false/>
    <key>NSExceptionDomains</key>
    <dict>
        <!-- Only listing your own domain without including RevenueCat -->
        <key>myapp.com</key>
        <dict>...</dict>
    </dict>
</dict>

5. Enable verbose logging to capture the full error

RevenueCat's debug logs include the HTTP status code and response body for failed requests, which makes it much easier to diagnose network errors:

swift
// Enable before configure() to capture all network traffic in logs
Purchases.logLevel = .verbose
Purchases.configure(withAPIKey: "appl_YOUR_KEY")

// In the console, look for lines like:
// [Purchases] - DEBUG: Network request to GET https://api.revenuecat.com/...
// [Purchases] - ERROR: Network error: The network connection was lost.
kotlin
// Enable before configure() — use VERBOSE for full network details
Purchases.logLevel = LogLevel.VERBOSE
Purchases.configure(
    PurchasesConfiguration.Builder(this, "goog_YOUR_KEY").build()
)

// In Logcat, filter by tag "RevenueCat" and look for:
// D/RevenueCat: Network request to GET https://api.revenuecat.com/...
// E/RevenueCat: Network error: java.net.SocketTimeoutException