---
title: "SKError codes and how to handle them"
description: "The comprehensive list of SKError codes on iOS. What issues can occur during transaction, actions needed from a developer's side, and alerts to be displayed"
url: "https://adapty.io/blog/ios-skerrordomain-error-codes/"
language: "en"
slug: "ios-skerrordomain-error-codes"
category: "adapty-updates"
date: "2021-08-30T00:00:00.000Z"
date_modified: "2026-01-17T16:00:15.000Z"
author: "Ben Gohlke"
---

# SKError codes and how to handle them

This is the final article in our series on implementing in-app purchases for iOS. Follow the links below to catch up on the previous ones:

1. [iOS in-app purchases, part 1: App Store Connect and project configuration](https://adapty.io/blog/how-to-set-up-ios-in-app-purchases/)
2. [iOS in-app purchases, part 2: purchase initialization and processing](https://adapty.io/blog/product-optimization-for-ios/)
3. [iOS in-app purchases, part 3: testing purchases in Xcode](https://adapty.io/blog/how-to-test-ios-in-app-purchases/)
4. [iOS in-app purchases, part 4: server-side receipt validation](https://adapty.io/blog/ios-in-app-purchase-server-side-validation/)
5. iOS in-app purchases: the list of SKError codes and how to handle them

In this post, I will address the various kinds of SKErrors you might encounter: what problems they reveal and what to do about them. For each error, we will work out three aspects: why it occurs, how to handle them, and what message to display in the error notification.

## SKError quick reference table

Use this table for quick lookup of error codes. For detailed explanations, see the sections below.

| **Code** | **Name** | **Cause** | **Developer action** |
| --- | --- | --- | --- |
| **0** | unknown | Unknown error occurred | Retry, check sandbox account |
| **1** | clientInvalid | User cannot make purchase | None required |
| **2** | paymentCancelled | User cancelled payment | Don't show error; consider win-back |
| **3** | paymentInvalid | Payment method declined | None required |
| **4** | paymentNotAllowed | Device cannot authorize payments | None required |
| **5** | storeProductNotAvailable | Product unavailable in region | Check Storefront API |
| **6-9** | Cloud service errors | Apple Music / cloud related | None required (rare) |
| **10** | unauthorizedRequestData | Missing IAP entitlement | Complete IAP setup in Xcode |
| **11-14** | Offer errors | Invalid offer config/signature | Fix offer in App Store Connect |
| **15-17** | Overlay errors | Purchase UI issues | Check UI presentation code |
| **18** | ineligibleForOffer | User doesn't qualify for offer | Show alternative offer |
| **19-20** | Platform/scene errors | Unsupported platform or wrong scene | Check platform requirements |

## What is SKErrorDomain?

SKErrorDomain is the error domain for StoreKit 1 errors in iOS. When an in-app purchase fails, the system returns an NSError with domain "SKErrorDomain" and a code from 0-20 indicating the specific failure reason. Each code corresponds to an SKError.Code enumeration value defined in Apple's StoreKit framework.

For each error in this guide, we cover three aspects: **why it occurs**, **how to handle it**, and **what message to display** to the user.

![Image](https://adapty.io/assets/uploads/2021/08/image.png)

## StoreKit 1 error codes (SKError)

### SKError.Code.unknown (Code 0)

**What it means:** An error occurred for an unknown reason. This is a catch-all for unspecified errors.

**How to fix it:** No direct action is usually needed. However, this error commonly occurs when testing in iOS 14+ simulators. Use StoreKit Configuration file for local testing or test on a real device. Also check if sandbox accounts are properly configured and App Store Connect agreements are accepted.

**User message:**"The purchase is unavailable for an unknown reason. Please try again later."

### SKError.Code.clientInvalid (Code 1)

**What it means:** The user cannot make a purchase. This typically happens when a child tries to buy something with parental controls enabled or when the Apple ID is restricted.

**How to fix it:** No action is needed from the developer's side. This is a user account restriction.

**User message:**"The purchase cannot be completed with this account."

### SKError.Code.paymentCancelled (Code 2)

**What it means:** The user got to the payment screen but changed their mind and clicked "cancel". This is one of the most common "errors" you'll encounter.

**How to fix it:** From a technical point of view, no error occurred and nothing needs to be done. From a marketing perspective, you could implement win-back strategies such as offering a discount or showing alternative pricing options.

**Important:** This error can also occur due to network timeouts during confirmation, Strong Customer Authentication (SCA) failures in the EU, or Ask to Buy rejection by a parent. Don't assume it's always intentional cancellation.

**User message:** Best practice is to show nothing. Optionally: "You have canceled your purchase. Would you like to try a subscription at a discounted price?"

### SKError.Code.paymentInvalid (Code 3)

**What it means:** The payment failed because the card was expired, there were insufficient funds, or another payment method issue occurred.

**How to fix it:** No action is needed from the developer's side.

**User message:**"Your purchase was declined. Please check your payment method and update if necessary."

### SKError.Code.paymentNotAllowed (Code 4)

**What it means:** The device is not allowed to authorize payments. This could be due to parental controls, enterprise/corporate policies, or the device/user not being authorized to make purchases.

**How to fix it:** No action is needed from the developer's side.

**User message:**"Purchases are disabled on this device. Please check your settings or parental controls."

### SKError.Code.storeProductNotAvailable (Code 5)

**What it means:** The user is trying to buy a product that is not available in their App Store region.

**How to fix it:** Check product availability using the Storefront API before displaying products. Filter out unavailable products from your UI.

**User message:**"This product is not available in your region. Please check your App Store region and try again."

### Cloud service errors (Codes 6-9)

These errors relate to cloud services like Apple Music:

- **Code 6 (cloudServicePermissionDenied):** User has not allowed access to cloud service information
- **Code 7 (cloudServiceNetworkConnectionFailed):** No internet connection during purchase
- **Code 8 (cloudServiceRevoked):** Device doesn't have access to StoreKit payment service (common in Sandbox)
- **Code 9 (privacyAcknowledgementRequired):** User hasn't acknowledged Apple's privacy policy for Apple Music

### SKError.Code.unauthorizedRequestData (Code 10)

**What it means:** Your app ID doesn't contain the required entitlement for using StoreKit.

**How to fix it:** Complete the IAP setup process: add the In-App Purchase capability in Xcode, ensure your bundle ID matches App Store Connect, and verify all agreements are accepted.

**User message:**"An error has occurred. Please try again later."

### Promotional offer errors (Codes 11-14)

**These errors relate to subscription promotional offers:**

- **Code 11 (invalidOfferIdentifier):** Offer ID not set up in App Store Connect or revoked. Fix: verify offer exists and is active in ASC.
- **Code 12 (invalidSignature):** Signature for promotional offer is incorrect. Fix: regenerate using correct subscription key (.p8 file). If using Adapty, verify your Subscription Key is properly configured.
- **Code 13 (missingOfferParams):** SKPaymentDiscount parameters are missing. Fix: ensure all required parameters are provided.
- **Code 14 (invalidOfferPrice):** Discounted price is higher than base price. Fix: update pricing in App Store Connect.

### Overlay and platform errors (Codes 15-20)

These errors relate to purchase UI presentation:

- **Code 15 (overlayCancelled):** User cancelled in the purchase overlay
- **Code 16 (overlayInvalidConfiguration):** Missing or incorrect IAP configuration
- **Code 17 (overlayTimeout):** Purchase overlay timed out waiting for response
- **Code 18 (ineligibleForOffer):** User doesn't meet eligibility criteria for the offer
- **Code 19 (unsupportedPlatform):** App running on unsupported platform or device
- **Code 20 (overlayPresentedInBackgroundScene):** Purchase overlay presented in wrong scene

## StoreKit 2 error handling

StoreKit 2, introduced in iOS 15, uses a completely different approach to error handling. Instead of error codes, it uses Swift enums with named cases. The key difference is that **user cancellation is not an error** in StoreKit 2—it's a separate case in the PurchaseResult enum.

| **Scenario** | **StoreKit 1 (Deprecated)** | **StoreKit 2** |
| --- | --- | --- |
| **User cancels purchase** | SKError.Code.paymentCancelled (2) | PurchaseResult.userCancelled |
| **Ask to Buy pending** | SKPaymentTransactionState.deferred | PurchaseResult.pending |
| **Network error** | SKError + NSURLErrorDomain | StoreKitError.networkError |
| **Unknown/system error** | SKError.Code.unknown (0) | StoreKitError.systemError |
| **Product unavailable in region** | SKError.Code.storeProductNotAvailable (5) | StoreKitError.notAvailableInStorefront |
| **Invalid offer signature** | SKError.Code.invalidSignature (12) | Product.PurchaseError.invalidOfferSignature |
| **User ineligible for offer** | SKError.Code.ineligibleForOffer (18) | Product.PurchaseError.ineligibleForOffer |
| **Transaction verification failed** | Manual receipt validation | VerificationResult.unverified |

### StoreKitError cases

StoreKitError handles general StoreKit errors:

- **unknown:** An unknown error occurred. Log for debugging, show generic retry message.
- **userCancelled:** User cancelled the operation. No message needed, return to previous state.
- **networkError(Error):** Network connection failed. Show connectivity message, allow retry.
- **systemError(Error):** System-level error. Log underlying error, show generic message.
- **notAvailableInStorefront:** Product unavailable in user's region. Hide product or show unavailable message.
- **notEntitled:** User not entitled to access. Check subscription status.

### Product.PurchaseError cases

Product.PurchaseError handles purchase-specific errors:

- **invalidQuantity:** Invalid purchase quantity. Ensure quantity is > 0.
- **productUnavailable:** Product not available. Refresh product list.
- **purchaseNotAllowed:** Device/user cannot make purchases. Inform user to check settings.
- **ineligibleForOffer:** User doesn't qualify for offer. Show alternative offer.
- **invalidOfferIdentifier:** Offer ID not configured. Check App Store Connect.
- **invalidOfferPrice:** Offer price invalid. Review pricing in ASC.
- **invalidOfferSignature:** Signature invalid. Regenerate with correct key.
- **missingOfferParameters:** Required parameters missing. Provide all offer parameters.

## Conclusion

Correct error handling improves user experience and can recover users who couldn't complete their purchase initially. The most common errors you'll encounter are unknown (0), paymentCancelled (2), and paymentInvalid (3).Error handling and purchase implementation are complex processes. **Adapty for iOS** makes implementing in-app purchases easier by handling these errors automatically, plus provides subscription analytics, cohort analysis, A/B tests for paywalls, and server-side receipt validation.

## FAQ

### What is the most common SKError?

The three most common errors are: SKError.Code.unknown (0) for general failures, SKError.Code.paymentCancelled (2) when users cancel purchases, and SKError.Code.paymentInvalid (3) when payment methods are declined.

### Why don't purchases work in the iOS Simulator?

The iOS Simulator has limited StoreKit support. Use StoreKit Configuration files for local testing (available since iOS 14). This lets you test the full purchase flow without network or App Store Connect. For production testing, use a physical device with sandbox accounts.

### How should I handle user cancellation?

Best practice is to NOT show an error message when users cancel. Simply dismiss the purchase UI and return to the previous state. In StoreKit 2, cancellation is a PurchaseResult case (not an error), making this cleaner to handle.

### How do I test error scenarios?

Use Xcode's StoreKit Configuration file to simulate various scenarios: enable "Fail Transactions" for error testing, configure "Ask to Buy" for pending states, and use "Interrupted Purchases" for partial transactions. The Transaction Manager lets you approve/decline transactions and trigger subscription events.

### What's the difference between Sandbox and StoreKit Testing?

StoreKit Testing (local) uses a configuration file in Xcode—no network, instant transactions, full control over errors. Sandbox testing connects to Apple's test servers—requires sandbox accounts, products must be in App Store Connect, simulates real-world conditions. Use local testing during development, sandbox for pre-release validation.
