Use this page with AI
Copy this into your coding assistant and add your request.
Read https://openiap.dev/docs/errors and https://openiap.dev/llms.txt. Follow the reading instructions, detailed reference, and linked guides relevant to my task before making changes.
Inspect my existing project and reuse its framework and conventions. Ask me for missing product decisions. Implement the requested behavior and run the applicable checks.
Show the working result, the commands and actual test results, and any remaining limitations. Keep your explanation brief.
My request: [describe what customers should be able to do]Error Codes
Error Structure
All purchase errors follow a consistent structure for easy handling. The PurchaseError shape is defined below.
React Native IAP rejects with an Error carrying these structured fields. Read error.code and the diagnostic properties directly; error.message is human-readable text. Rebuild the native app after upgrading to 17.3.0 to enable structured error transport. Older native binaries use the compatibility decoder through 17.x. The string parser parseErrorStringToJsonObj and cancellation detection from strings or Error.message without a code are deprecated and removed in react-native-iap 18.0.0. Pass structured errors to isUserCancelledError; these remain supported.
struct PurchaseError: Codable {
let code: ErrorCode // Generated error enum
let message: String // Human-readable message
let productId: String? // Related product SKU (if applicable)
let debugMessage: String? // Raw diagnostic (e.g. StoreKit error.localizedDescription)
let responseCode: Int? // Android QueryProduct BillingResult.responseCode
let productIds: [String]? // Android QueryProduct requested IDs
let productType: String? // Android BillingClient product type
let isEmptyProductList: Bool? // Android QueryProduct returned no products
let subResponseCodeAndroid: SubResponseCodeAndroid? // Play purchase-update detail
}Common Error Codes
User Action Errors
| Code | Description | Action |
|---|---|---|
UserCancelled | User cancelled the purchase flow | No action needed, expected behavior |
UserError | User-related error during purchase | Check user account status |
DeferredPayment | Payment was deferred (pending family approval, etc.) | Wait for payment approval |
Interrupted | Purchase flow was interrupted | Retry the purchase |
Product Errors
| Code | Description | Action |
|---|---|---|
ItemUnavailable | Product not available in store | Check product configuration in store console |
SkuNotFound | SKU not found in product list | Verify SKU exists in store configuration |
SkuOfferMismatch | SKU offer ID mismatch | Check offer configuration for the SKU |
QueryProduct | Failed to query product details | Check product IDs and retry |
AlreadyOwned | Item already owned by user | Restore purchases to unlock content |
ItemNotOwned | Item not owned by user | Purchase the item first |
DuplicatePurchase | Duplicate purchase-updated event for the same token (Android) | Deduplicate by purchase token and ignore duplicates |
user-cancelled. Some wrappers still normalize legacy E_USER_CANCELLED aliases for compatibility, but new app code should compare the generated ErrorCode.UserCancelled enum value or the user-cancelled wire value.QueryProduct errors from openiap-google 2.1.4 and later include the Google Play Billing responseCode, debugMessage, requested productIds, requested productType, and isEmptyProductList when available. Use these diagnostics to distinguish Play Console product setup issues from transient BillingClient failures. In OpenIAP 2.3.0 / openiap-google 2.3.1, purchase-update failures also carry subResponseCodeAndroid (requires Play Billing 8.0+) when Play Billing provides a more specific reason such as insufficient funds or offer ineligibility.not-prepared. See the error-code upgrade notes.Network & Service Errors
| Code | Description | Action |
|---|---|---|
NetworkError | Network connection error | Check internet connection and retry |
ServiceError | Store service error | Wait and retry, check store service status |
RemoteError | Remote server error | Check server logs, retry request |
InitConnection | Failed to initialize store connection | Check store service availability and retry |
ServiceDisconnected | Store service disconnected | Reconnect to the store service |
ServiceTimeout | Request reached the billing service timeout (Google Play 8.x+), or a launched Android purchase reported no result: Google Play after 15 minutes and an owned-purchase check, Meta Horizon after about 5 minutes | Retry the call after a short delay; before retrying a purchase, call getAvailablePurchases in case it completed |
ConnectionClosed | Connection to store service was closed | Reinitialize connection and retry |
IapNotAvailable | In-app purchase service not available | Check device settings and IAP availability |
BillingUnavailable | Billing service is unavailable | Check Google Play/App Store availability |
FeatureNotSupported | Requested feature not supported | Check device/OS version compatibility |
SyncError | Synchronization error with store | Retry synchronization |
Validation Errors
| Code | Description | Action |
|---|---|---|
PurchaseVerificationFailed | Purchase verification failed | Check verification logic, retry validation |
PurchaseVerificationFinished | Purchase verification already completed | Verification already completed, check records |
PurchaseVerificationFinishFailed | Failed to finish purchase verification | Check verification state and retry |
TransactionValidationFailed | Transaction validation failed | Verify transaction data and retry |
EmptySkuList | Empty SKU list provided | Provide at least one SKU to query |
Error Handling Examples
Error Handling Pattern
Implement error handlers that respond appropriately to each error type:
- User Cancellation - Silent handling, no alerts
- Product Issues - Inform user about availability
- Ownership Conflicts - Trigger purchase restoration
- Network Errors - Suggest retry with backoff
- Unknown Errors - Generic fallback message
User Cancellation
Treat user cancellation as an expected result of the purchase flow. Do not show an error alert, retry automatically, or report it as a service failure.
import { ErrorCode } from 'react-native-iap';
function isUserCancellation(error: { code?: unknown }) {
return error.code === ErrorCode.UserCancelled || error.code === 'user-cancelled';
}Retry Strategy
Implement retry logic for transient errors:
| Error Type | Can Retry | Strategy |
|---|---|---|
| NetworkError | Yes | Exponential backoff (2^n seconds) |
| ServiceError | Yes | Linear backoff (n * 5 seconds) |
| RemoteError | Yes | Fixed delay (10 seconds) |
| ConnectionClosed | Yes | Reinitialize and retry |
| SyncError | Yes | Exponential backoff |
| UserCancelled | No | Do not retry |
| AlreadyOwned | No | Restore instead |
| DeferredPayment | No | Wait for approval |
| NotPrepared | No | Initialize connection first |
Platform-Specific Error Handling
iOS Error Codes
| Native Code | Mapped Error | Description |
|---|---|---|
| 0 | Unknown | Unknown error |
| 1 | UserCancelled | User cancelled transaction |
| 2 | NetworkError | Network unavailable |
| 3 | ItemUnavailable | Product not available |
| 4 | ServiceError | App Store service error |
| 5 | PurchaseVerificationFailed | Purchase verification failed |
| 6 | AlreadyOwned | Product already purchased |
ErrorCode Enum (Unified)
Complete list of error codes that can be returned by the IAP library.
enum OpenIapError: Error {
case unknown
case userCancelled
case userError
case itemUnavailable
case remoteError
case networkError
case serviceError
case purchaseVerificationFailed
case purchaseVerificationFinished
case purchaseVerificationFinishFailed
case notPrepared
case notEnded
case alreadyOwned
case developerError
case billingResponseJsonParseError
case deferredPayment
case interrupted
case iapNotAvailable
case purchaseError
case syncError
case transactionValidationFailed
case activityUnavailable
case alreadyPrepared
case pending
case connectionClosed
case initConnection
case serviceDisconnected
case serviceTimeout
case queryProduct
case skuNotFound
case skuOfferMismatch
case itemNotOwned
case billingUnavailable
case featureNotSupported
case emptySkuList
case duplicatePurchase
}Testing Error Scenarios
Testing Error Scenarios
iOS Sandbox Testing
| Test Account | Simulated Error |
|---|---|
| test.purchase.failed@example.com | PurchaseVerificationFailed |
| test.purchase.cancelled@example.com | UserCancelled |
| test.purchase.unavailable@example.com | ItemUnavailable |
Android Testing Methods
| Test Method | Description |
|---|---|
| License Testing | Add test accounts in Google Play Console for real purchases without charges |
| Internal Testing Track | Deploy to internal testers for production-like testing |
| Closed Testing | Test with limited group of users before production release |
| Test Cards (Sandbox) | Use test payment methods configured in Play Console |
⚠️ Important: Static test product IDs like android.test.purchased are deprecated and no longer work. Use real product IDs with test accounts instead. See Releases page for details →
Development Testing
For development testing, consider implementing mock error generators that can simulate various error conditions without requiring actual purchases. This allows you to:
- Test error handling UI flows
- Verify analytics tracking
- Validate retry logic
- Ensure proper error recovery