Troubleshooting
Find the cause of common Swift SDK problems by symptom, and fix them.
Each section starts from what you see and lists the causes in the order to check them. Most
failures the SDK handles on its own don't throw; turn on options.debug = true or set
onDiagnostic to see their codes.
Products are empty
getProducts() returns [], or a product is missing.
- No App Store mapping. Only products mapped to an App Store product are returned. In Studio, open the product and check its App Store product ID. See Products and perks.
- The App Store doesn't return it. The product isn't Ready to Submit or approved in App Store
Connect, its ID differs, the Paid Apps agreement isn't active, or the bundle ID doesn't match. A
missing product throws
INVALID_PRODUCT_IDwhen you buy it by slug. - Wrong key or project. The SDK reads the catalog of the project its publishable key belongs to.
A key Voidhash rejects pauses requests and reports
AUTHENTICATION_FAILED. - Voidhash unreachable on first launch. With no catalog cached,
getProducts()returns[]at once while Voidhash is known to be down. It loads on the next call once Voidhash answers. - App Store unreachable. The call throws
FAILED_TO_GET_PRODUCTS.
In development mode, every product is returned without the App Store.
The paywall doesn't appear
presentFlow returns .failed with a code:
| Code | Fix |
|---|---|
FLOW_RENDERER_MISSING | Add VoidhashUI and set options.paywallRenderer = VoidhashUIPaywallRenderer(). See Add the paywall renderer. |
FLOW_NOT_ASSIGNED | Check that the slug you pass matches the location in Studio exactly, that the location isn't archived, and that a deployed paywall is assigned to it. If the deployed build uses components this app's SDK can't run, update the SDK. |
FLOW_UNAVAILABLE | The device has never loaded the location and can't reach Voidhash. Retry when online, or preload it. |
FLOW_UNSUPPORTED_PACKAGE | The paywall needs a newer SDK. Update the SDK and rebuild the renderer engine. |
FLOW_PRESENTATION_FAILED | No window was available, for example when called before the first scene connects. Present after your UI appears, or pass a view controller to presentFlow(placement:from:). |
FLOW_DISABLED | The client was configured with enabled = false or shut down. |
A paywall you just deployed can take up to an hour to reach a device that already cached the location. Reinstall the app to skip the cache while testing. If the engine fails to link, rebuild it with the steps in Add the paywall renderer; in the Simulator, it runs only on Apple silicon Macs.
A paywall button that does nothing usually asks for an action you didn't grant in
flowCapabilities; onWarning names the capability.
A purchase doesn't unlock the perk
The purchase succeeds but hasActivePerk stays false.
- The product doesn't carry the perk. Check in Studio that the product you sold has the perk attached, and that you pass the perk's slug, not its name or ID.
- Voidhash hasn't accepted the receipt.
purchasereturned.deferred, orpendingTransactionCount()is above zero. The SDK keeps sending it;syncPendingWork()sends it now. - Voidhash rejected the receipt. The
TRANSACTION_REJECTEDdiagnostic gives the reason, most often that the App Store isn't connected (Help center) for this bundle ID. - Wrong environment. A test key doesn't see production purchases, and a production key sees store sandbox purchases only while they unlock production apps. See Store sandbox in production apps.
- Another identity. The purchase belongs to the distinct ID that made it. After
signOut(), or on a new install, the new anonymous ID has no access until the user identifies or restores. - A stale snapshot. Read with
getCurrentPerson(forceFetch: true)to rule out caching.
Sandbox and TestFlight purchases
- Purchases from an Xcode StoreKit configuration file never reach Voidhash. The App Store can't
verify them; the SDK reports
STOREKIT_TESTING_TRANSACTION_SKIPPED. Remove the StoreKit configuration from your scheme, or use development mode. - Studio doesn't show the purchase. Store sandbox purchases, including TestFlight and App Review, appear under the Sandbox environment; simulated ones under Test.
- TestFlight testers get access in production. Store sandbox purchases unlock apps on the production key by default, so App Review can open what it buys. See Store sandbox in production apps.
- Renewals arrive fast. App Store sandbox subscriptions renew on an accelerated schedule.
More in Test purchases (Help center).
Purchases are finished twice in observer mode
Another billing SDK owns purchases, and transactions are finished by both, or purchases throw
READ_ONLY_PURCHASE_NOT_ALLOWED.
- Set
options.readOnly = truewhile another SDK owns purchases. Without it, Voidhash also finishes the transactions it observes. - In observer mode, Voidhash never finishes a transaction. Keep finishing them in your billing SDK's
code, including those from
Transaction.updates. READ_ONLY_PURCHASE_NOT_ALLOWEDmeans a purchase started through Voidhash, from your code or a Voidhash paywall. Buy through your billing SDK, or switch ownership withsetReadOnly(false).
See Run in observer mode.
Events don't show up
- Consent. While consent is
.deniedor.pending, nothing is recorded; the first skipped call reportsANALYTICS_CONSENT_DENIED. - Not sent yet. Events go in batches every 5 seconds while the app is in the foreground. Call
flush()to send them now. - Wrong environment. Events from the test key appear under Test in Studio.
- Rejected key.
AUTHENTICATION_FAILEDpauses all requests until the key works again. - Dropped. More than 1,000 undelivered events drop the oldest (
ANALYTICS_EVENT_DROPPED), and Voidhash drops an event it refuses as invalid.
Check your data shows events Voidhash rejected; Rejected after capture lists the reasons.
Next steps
- Errors
- Troubleshooting across platforms
- Discord when you're stuck.
Something wrong or missing on this page? Tell us by email or on Discord.