What does Product.PurchaseError.invalidOfferIdentifier mean?
Product.PurchaseError.invalidOfferIdentifier means the promotional offer identifier you passed in the purchase options is not valid.
Quick facts#
| Error | Product.PurchaseError.invalidOfferIdentifier |
| Where | StoreKit 2, Swift (Product.purchase(options:)) |
| Available since | iOS 15.0, iPadOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0 |
| What the vendor says | The promotional offer identifier provided in the purchase options is invalid. |
Cause#
- The identifier in
.promotionalOffer(offerID:keyID:nonce:signature:timestamp:)does not match a promotional offer the App Store knows for this product. - Apple says promotional offers must first be set up in App Store Connect, and the offers on a product appear in its
subscription.promotionalOffersarray (promotionalOffer option). - A typo, an offer attached to another product or an offer you never created all produce this error.
Fix#
- Read the offer ID from
product.subscription?.promotionalOffersinstead of typing it by hand. - Compare the ID with the offer in App Store Connect, character by character.
- Make sure the offer belongs to the same subscription product you are buying.
- Sign the offer on your server only after you have picked a valid offer ID, because the ID is part of the signed text.
Example#
Swift
import StoreKit
// Take the offer ID from the product itself, so the identifier you sign and send always exists.
func offerID(for product: Product) -> String? {
product.subscription?.promotionalOffers.first?.id
}
func buy(_ product: Product, options: Set<Product.PurchaseOption>) async {
do {
_ = try await product.purchase(options: options)
} catch Product.PurchaseError.invalidOfferIdentifier {
print("The offer ID is not one of \(product.id)'s promotional offers")
} catch {
print("Purchase failed: \(error)")
}
}Compile-checked: npm run check:snippets type-checks this snippet with swiftc against the Apple SDK (macOS target, checked 2026-10-03).
How the RevenueDot SDK reports it#
The RevenueDot purchases-ios fork maps invalidOfferIdentifier, invalidOfferPrice, invalidOfferSignature and missingOfferParameters to one code, ErrorCode.invalidPromotionalOfferError (code 34). Read the underlying error to see which of the four it was.
Related#
- What does Product.PurchaseError.invalidOfferSignature mean?
- What does Product.PurchaseError.ineligibleForOffer mean?
- What does storeProblemError (code 2) mean in the purchases SDK?
Source#
- Apple: promotionalOffer purchase option
- Apple: Product.PurchaseError.invalidOfferIdentifier
- Apple: Product.PurchaseError
- RevenueDot purchases-ios: StoreKitError+Extensions.swift (how the SDK maps StoreKit 2 errors)
Checked: 2026-10-03