SKErrorDomain Error 2 in iOS apps and how to fix it

Last updated January 3, 2026 
by 
Ilia Lotarev
Published July 17, 2023 
Last updated January 3, 2026
9 min read
SKErrorDomain Code2 In IOS Apps And How To Fix It

Sometimes, even creating the perfect app and acquiring users is not enough. The “last mile” issues can rob you of your potential revenue, one of them being iOS errors like SKErrorDomain Error 2. It’s more common than you might think — so common, in fact, that we decided to write a comprehensive guide about it.

Here, we’re going to dive deep into what this error is all about, how it differs between StoreKit 1 and StoreKit 2, and share foolproof solutions to help you fix it in no time. The goal is to enhance your app’s reliability, boost user satisfaction, and eliminate any revenue drainers. Let’s get started.

What is SKErrorDomain Code=2

According to the Apple Developer documentation, SKError 2 is also known as SKError.paymentCancelled. It occurs when a person gets to the payment pop-up (that is, is asked to confirm a purchase or a subscription), and then declines it.

Technically, it’s not an error, as the cancellation is often triggered by the user. However, this error can also pop up regardless of user actions and be caused by an outside factor — and this article covers explicitly such cases.

Important: With iOS 18, Apple has officially deprecated all StoreKit 1 APIs. If you’re still using the original StoreKit framework, now is the time to migrate to StoreKit 2.

Image

StoreKit 1 vs StoreKit 2: How error handling differs

Before we dive into troubleshooting, it’s crucial to understand how payment cancellation is handled differently in each framework version.

StoreKit 1 vs StoreKit 2

AspectStoreKit 1 (Deprecated)StoreKit 2 (Recommended)
Error representationSKErrorDomain Code=2PurchaseResult.userCancelled
Swift typeSKError.paymentCancelledEnum case in Product.PurchaseResult
Handling mechanismSKPaymentTransactionObserver delegateasync/await pattern
Minimum iOS versioniOS 3+iOS 15+
API statusDeprecated in iOS 18Actively maintained
VerificationManual receipt validationAutomatic JWS verification
Image 1

What can cause SKErrorDomain Code=2

There are several potential triggers for this error. Understanding the root cause is essential for proper handling.

CauseDescriptionIs user action?Solution
User taps “Cancel”Explicit cancellation in payment sheetYesDon’t show error
Apple ID login cancelledUser cancels during sign-in promptYesDon’t show error
Active subscription existsAttempting to buy already active productNoOffer “Restore Purchases”
SCA rejection (EU)Strong Customer Authentication declinedPartialInform about retry
Ask to Buy declinedParent/guardian rejected purchaseNoShow “declined” message
Network timeoutConnection lost during confirmationNoSuggest retry
Sandbox account issuesInvalid or expired test accountNoCreate new sandbox user
Payment method invalidExpired card or insufficient fundsNoPrompt to update payment

Network issues

Network issues can be a significant catalyst for the SKErrorDomain Code=2. The StoreKit framework heavily relies on consistent network connectivity with little to no lag to perform transactions effectively. Unstable or unreliable connections — for example, using a hotspot or a free VPN — can interfere with the transmission of data.

StoreKit configuration

The StoreKit framework is the core engine driving your in-app transactions, and any mistake in this setup can readily lead to the SKErrorDomain Code=2 error. An incorrectly defined product ID, for example, can trigger the error. Product IDs should match precisely with those defined in App Store Connect; any discrepancy can create conflicts, leading to transaction failures.

Strong Customer Authentication (SCA) in Europe

For users in the European Economic Area, Strong Customer Authentication requires all payments to be verified. In this case, the current transaction may go to a failed state initially. Once payment is approved through the bank’s verification process, a new transaction with the purchased state is created. This behavior can sometimes manifest as a Code=2 error.

All SKError codes reference

For context, here’s the complete list of SKError codes:

CodeNameDescription
0unknownAn unknown error occurred
1clientInvalidClient is not allowed to make the request
2paymentCancelledUser cancelled the payment request
3paymentInvalidPayment parameters are invalid
4paymentNotAllowedDevice is not allowed to make payments
5storeProductNotAvailableProduct is not available in the store
6cloudServicePermissionDeniedUser hasn’t allowed access to cloud service
7cloudServiceNetworkConnectionFailedCouldn’t connect to network
8cloudServiceRevokedUser has revoked cloud service permission
9privacyAcknowledgementRequiredUser must acknowledge privacy policy
10unauthorizedRequestDataApp is not entitled for request
11invalidOfferIdentifierOffer identifier is invalid
12invalidSignatureCryptographic signature is invalid
13missingOfferParamsRequired offer parameters are missing
14invalidOfferPriceOffer price is invalid
15overlayCancelledOverlay was cancelled
16overlayInvalidConfigurationOverlay configuration is invalid
17overlayTimeoutOverlay request timed out
18ineligibleForOfferUser is ineligible for the offer
19unsupportedPlatformPlatform doesn’t support this feature
20overlayPresentedInBackgroundSceneOverlay presented in background

How to troubleshoot SKErrorDomain Error 2

Once you’ve run into the issue, it makes sense to go through every detail of the process to make sure everything is good on your part.

StepWhat to checkMethodExpected result
1Product IDs match App Store ConnectXcode Console logsNo “Invalid product identifier” errors
2Transactions finish properlytransaction.finish() callsAll transactions completed
3Network is stableNetwork Link ConditionerNo timeouts
4Sandbox account is validSettings → App Store → SandboxAccount active
5iOS/Xcode versions currentAbout This Mac / XcodeLatest versions
6StoreKit Configuration set upProduct → Scheme → StoreKit ConfigConfiguration selected
7Receipt refresh worksSKReceiptRefreshRequestReceipt retrieved

Sandbox environment troubleshooting

Here’s what you should do while in the sandbox environment:

Review StoreKit configuration. Begin by checking your app’s StoreKit configuration. Ensure all product IDs in your code match the ones set in App Store Connect. Misconfigured or mismatched IDs often result in errors in the sandbox environment.

Examine transaction handling. StoreKit handles a sequence of states for each transaction. Each state requires specific handling in your code, and proper handling of these states can eliminate errors. Review your transaction handling code for completeness and accuracy.

Ensure you’re finishing transactions upon success/failure. As we’ve explained before, the general cause of the error occurs at ‘the last mile’. Make sure each payment, even a canceled one, finishes ‘properly’ in your code.

Test network stability. Although it’s a sandbox environment, network conditions still matter. Verify that your testing device has a stable and strong internet connection.

Use Apple’s StoreKit testing tools. Apple provides specific tools to test StoreKit locally in Xcode and the sandbox. These tools allow you to simulate transactions, helping you to identify potential errors. With Xcode 16 and later, you can also test purchase intents for promoted purchases.

Check OS and app versions. Make sure the app and iOS versions are up-to-date. Each new iOS update brings some new terms and rules to the StoreKit, so you would benefit from having the latest one.

Production environment troubleshooting

With real-life scenarios, you get some extra steps — and extra sources of information.

Monitor user feedback. Users can often provide the first indication of an issue. Pay close attention to app reviews, support requests, and social media channels where users might report problems.

Analyze error logs. Use your app’s error logging system to gather information about where and when the error occurs. Troubleshooting becomes that much easier when you can pinpoint a specific page or an SKU that causes the problem.

Make sure the IAP hasn’t been purchased before. One of the key real-life causes reported by the developers occurred when a user tried to purchase an already active subscription. Always have the ‘Restore Purchases’ button at the ready and ask users to try it if they run into any problems.

Check for SCA-related issues. If you have significant user base in the European Economic Area, verify that your app properly handles Strong Customer Authentication flows.

How to submit a bug report

If you’ve exhausted all troubleshooting options and believe you’ve found a StoreKit bug, here’s how to report it:

  1. Access either the Feedback Assistant website or its app on your Mac.
  2. Sign in with the Apple ID linked to your Developer Program membership.
  3. Click the “New Feedback” button.
  4. Make sure to fill out the feedback form with as much detailed information as you can. Include the specific error (SKErrorDomain Error 2), during what process the error occurs, and any important pieces of code or log extracts.
  5. Attach any relevant screenshots or data that can provide more context about the problem.
  6. Once you’ve filled out the form, review your submission for any errors or missing information, then hit “Submit”.

Apple will proceed to examine your feedback. This may take several days, but it’s often rewarded with a potential resolution.

Image 2

How to prevent SKErrorDomain Code=2

Implementing preventive measures and following best practices can significantly reduce the likelihood of encountering the SKErrorDomain Code=2 error in future projects.

PracticePriorityImplementation
Migrate to StoreKit 2CriticalUse async/await APIs for iOS 15+
Validate product IDsHighMatch exactly with App Store Connect
Handle all purchase statesHighInclude userCancelled, pending cases
Implement Restore PurchasesHighVisible button, test regularly
Add comprehensive loggingMediumTrack errors by product/step
Monitor user feedbackMediumApp reviews, support channels
Test in sandbox regularlyMediumUse fresh sandbox accounts
Handle SCA gracefullyMediumEU-specific messaging

Recommended implementation

Migrate to StoreKit 2. With StoreKit 1 now deprecated in iOS 18, migrating to StoreKit 2 is no longer optional for long-term app maintenance. StoreKit 2 provides cleaner error handling, automatic transaction verification, and modern Swift concurrency support.

Test StoreKit configuration thoroughly. Test your StoreKit configuration before deploying your app. Make sure that all product IDs match those in App Store Connect and that all transactions process correctly during testing.

Handle all transaction states. Ensure that your code accounts for all possible transaction states. Since SKErrorDomain Code=2 has to do with a ‘canceled’ purchase, make sure your app finishes every transaction properly.

Implement comprehensive error logging. A comprehensive error logging system can help you identify and address issues promptly. By tracking exactly what product or purchase step the errors occur on, you’ll greatly simplify the troubleshooting process.

Regularly review user feedback. Stay on top of user reviews and feedback. Users are often the first to spot issues, and their feedback can help you identify and resolve problems early. Don’t shy away from asking for screenshots or engaging with users on social media.

Keep your app updated. Update your application and ensure it’s compatible with the latest iOS versions. Compatibility issues with newer OS versions can often lead to unexpected errors.

Conclusion

Effectively managing the SKErrorDomain Code=2 error is a critical aspect of maintaining a robust and user-friendly iOS application. This error can disrupt in-app transactions, potentially leading to a diminished user experience and even revenue loss. Luckily, it’s often a minor issue that can be easily tracked and resolved.

With the deprecation of StoreKit 1 in iOS 18, now is the perfect time to migrate to StoreKit 2, which offers cleaner error handling through PurchaseResult.userCancelled instead of error codes, automatic transaction verification, and modern Swift concurrency patterns.

A seamless user experience is the cornerstone of any successful app, and ensuring error-free transactions is an integral part of this. With the insights shared in this article, you’re now well-equipped to manage the SKErrorDomain Code=2, contributing to a more robust and reliable app that users can truly appreciate.

FAQ

It’s an error that occurs during the purchase process when a user fails to finish the purchase by confirming the payment. While often triggered by user action, it can also result from network issues, SCA verification failures, or attempting to purchase an already active subscription.

In StoreKit 2, payment cancellation is not represented as an error but as a separate case PurchaseResult.userCancelled in the purchase result enum. This makes handling cleaner and more intuitive since cancellation is treated as a normal flow rather than an exception.

There are several possible reasons: Strong Customer Authentication (SCA) rejection in the EU, Ask to Buy declined by a parent, network timeout during confirmation, attempting to buy an already active subscription, or sandbox account issues during testing.

The best practice is to not show any error message when the user cancels. Simply dismiss the purchase UI and return to the previous state. Showing an error alert for user-initiated cancellation creates a poor user experience.

Use StoreKit Configuration file and the Transaction Manager in Xcode. You can simulate cancellations and various error states. With Xcode 16+, you can also test purchase intents for promoted purchases, including App Store offers.

This is a rare but known issue. Use the App Store Server API to verify the transaction status server-side. If the payment was indeed processed, you may need to manually unlock the content and investigate why the client received an incorrect error.

Yes, if the error occurs because the user is trying to purchase an already active subscription or non-consumable product. Always implement and prominently display a “Restore Purchases” button, and suggest users try it when encountering purchase issues.
Ilia Lotarev
Head of Strategy at an IM/UA agency, content contributor for Adapty
iOS

On this page

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