Handle errors in Kotlin Multiplatform SDK
This page covers error handling in the Adapty Kotlin Multiplatform SDK.
Error handling basics
Adapty SDK methods are suspend functions that return an AdaptyResult, which is either a success or an error. Handle both cases with onSuccess and onError:
import com.adapty.kmp.Adapty
Adapty.getProfile()
.onSuccess { profile ->
// Handle success
}
.onError { error ->
// Handle error
println("Adapty error: ${error.message}")
}
AdaptyResult is a sealed class, so you can also branch on it directly when you need the result as a value:
import com.adapty.kmp.models.AdaptyResult
when (val result = Adapty.getProfile()) {
is AdaptyResult.Success -> {
val profile = result.value
// Handle success
}
is AdaptyResult.Error -> {
val error = result.error
// Handle error
}
}
Common error codes
AdaptyError.code is an AdaptyErrorCode enum value, not a number — compare it against the enum constant, not against the numeric code. The numbers below are shown only because they are what a log line or a support ticket usually carries.
| Error code | Number | Description | Solution |
|---|---|---|---|
AdaptyErrorCode.NO_PRODUCT_IDS_FOUND | 1000 | None of the products in the paywall are available in the store. | See Fix for Code-1000 noProductIDsFound error. |
AdaptyErrorCode.CANT_MAKE_PAYMENTS | 1003 | In-app purchases are not allowed on this device. | See Fix for Code-1003 cantMakePayments error. |
AdaptyErrorCode.PRODUCT_NOT_FOUND | 22 | The product requested for purchase is not available in the store. | Check that the product is configured in the store and in the Adapty Dashboard. |
AdaptyErrorCode.NETWORK_FAILED | 2005 | The network request failed. | Ask the user to check their connection, or retry the call. |
AdaptyErrorCode.ADAPTY_NOT_INITIALIZED | 20 | The SDK was not activated before the call. | Wait for Adapty.activate to complete before any other SDK call. |
For the full list of codes, see AdaptyErrorCode in the SDK models reference.
Handle specific errors
Network errors
import com.adapty.kmp.Adapty
import com.adapty.kmp.models.AdaptyErrorCode
Adapty.getFlow("YOUR_PLACEMENT_ID")
.onSuccess { flow ->
// Use the flow
}
.onError { error ->
when (error.code) {
AdaptyErrorCode.NETWORK_FAILED -> {
// Network error - show offline message
showOfflineMessage()
}
else -> {
showErrorMessage(error.message)
}
}
}
Purchase errors
makePurchase reports failures and outcomes separately. An AdaptyResult.Error means the call failed; a successful result returns an AdaptyPurchaseResult that tells you whether the user completed the purchase, canceled it, or left it pending:
import com.adapty.kmp.Adapty
import com.adapty.kmp.models.AdaptyErrorCode
import com.adapty.kmp.models.AdaptyPurchaseResult
Adapty.makePurchase(product)
.onSuccess { purchaseResult ->
when (purchaseResult) {
is AdaptyPurchaseResult.Success -> showSuccessMessage()
AdaptyPurchaseResult.UserCanceled -> {
// The user dismissed the store sheet — not an error
}
AdaptyPurchaseResult.Pending -> {
// Awaiting an out-of-band payment, such as a prepaid plan
showPendingMessage()
}
}
}
.onError { error ->
when (error.code) {
AdaptyErrorCode.CANT_MAKE_PAYMENTS -> showPaymentNotAvailableMessage()
AdaptyErrorCode.PRODUCT_NOT_FOUND -> showProductNotAvailableMessage()
else -> showPurchaseErrorMessage(error.message)
}
}
Error recovery strategies
Retry on network errors
Because SDK methods are suspend functions, a retry loop is a plain loop with delay between attempts:
import com.adapty.kmp.Adapty
import com.adapty.kmp.models.AdaptyErrorCode
import com.adapty.kmp.models.AdaptyFlow
import com.adapty.kmp.models.AdaptyResult
import kotlinx.coroutines.delay
suspend fun getFlowWithRetry(placementId: String, maxRetries: Int = 3): AdaptyFlow? {
repeat(maxRetries) { attempt ->
when (val result = Adapty.getFlow(placementId)) {
is AdaptyResult.Success -> return result.value
is AdaptyResult.Error -> {
if (result.error.code != AdaptyErrorCode.NETWORK_FAILED) {
showErrorMessage(result.error.message)
return null
}
delay(1000L * (attempt + 1))
}
}
}
return null
}
Fall back to cached data
You don’t need to cache flows yourself. Adapty SDK caches them on the device and returns the cached copy when the network fails, so pass a fetch policy instead of writing your own cache:
import com.adapty.kmp.Adapty
import com.adapty.kmp.models.AdaptyPaywallFetchPolicy
Adapty.getFlow(
placementId = "YOUR_PLACEMENT_ID",
fetchPolicy = AdaptyPaywallFetchPolicy.ReturnCacheDataElseLoad
)
.onSuccess { flow ->
// Use the flow
}
.onError { error ->
showErrorMessage(error.message)
}
With ReturnCacheDataElseLoad, users on a patchy connection get a flow faster, at the cost of possibly not seeing the latest version. If neither the network nor the cache produces a flow, the SDK falls back to the fallback file you bundle with the app.
Next steps
- Fix for Code-1000 noProductIDsFound error
- Fix for Code-1003 cantMakePayments error
- Complete API reference - Full SDK documentation