SKError codes and how to handle them

Last updated January 17, 2026 
by 
Ben Gohlke
Published August 30, 2021 
Last updated January 17, 2026
7 min read
613eddd1ca838d069e5d8ad2 Ios Tutorial 5 Skerrors

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
  2. iOS in-app purchases, part 2: purchase initialization and processing
  3. iOS in-app purchases, part 3: testing purchases in Xcode
  4. iOS in-app purchases, part 4: server-side receipt 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.

CodeNameCauseDeveloper action
0unknownUnknown error occurredRetry, check sandbox account
1clientInvalidUser cannot make purchaseNone required
2paymentCancelledUser cancelled paymentDon’t show error; consider win-back
3paymentInvalidPayment method declinedNone required
4paymentNotAllowedDevice cannot authorize paymentsNone required
5storeProductNotAvailableProduct unavailable in regionCheck Storefront API
6-9Cloud service errorsApple Music / cloud relatedNone required (rare)
10unauthorizedRequestDataMissing IAP entitlementComplete IAP setup in Xcode
11-14Offer errorsInvalid offer config/signatureFix offer in App Store Connect
15-17Overlay errorsPurchase UI issuesCheck UI presentation code
18ineligibleForOfferUser doesn’t qualify for offerShow alternative offer
19-20Platform/scene errorsUnsupported platform or wrong sceneCheck 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

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.

ScenarioStoreKit 1 (Deprecated)StoreKit 2
User cancels purchaseSKError.Code.paymentCancelled (2)PurchaseResult.userCancelled
Ask to Buy pendingSKPaymentTransactionState.deferredPurchaseResult.pending
Network errorSKError + NSURLErrorDomainStoreKitError.networkError
Unknown/system errorSKError.Code.unknown (0)StoreKitError.systemError
Product unavailable in regionSKError.Code.storeProductNotAvailable (5)StoreKitError.notAvailableInStorefront
Invalid offer signatureSKError.Code.invalidSignature (12)Product.PurchaseError.invalidOfferSignature
User ineligible for offerSKError.Code.ineligibleForOffer (18)Product.PurchaseError.ineligibleForOffer
Transaction verification failedManual receipt validationVerificationResult.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

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.

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.

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.

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.

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.

Ben Gohlke
Developer Advocate
iOS
Tutorial

On this page

Ready to create your first paywall with Adapty?
Build money-making paywalls without coding
Get started for free