> ## Documentation Index
> Fetch the complete documentation index at: https://primer.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# PrimerError

> Error details with a diagnostics ID and recovery suggestion

> [!WARNING]
>
> Primer Checkout iOS SDK is currently in **beta** (v3.0.0-beta.8).
> The API is subject to change before the stable release.

`PrimerError` is the error type delivered when a payment or checkout fails. Every error carries a stable `errorId` for programmatic handling, a `diagnosticsId` to share with Primer support, and (where applicable) a `recoverySuggestion` describing how to recover.

```swift theme={"dark"}
public enum PrimerError: PrimerErrorProtocol {
    public var errorId: String { get }
    public var diagnosticsId: String { get }
    public var recoverySuggestion: String? { get }
    public var plainDescription: String? { get }
    public var errorDescription: String? { get }
    public var errorUserInfo: [String: Any] { get }
    public var underlyingErrorCode: String? { get }
}
```

> [!NOTE]
>
> `PrimerError` conforms to `LocalizedError` and `CustomNSError`, so you also get `localizedDescription` for free and can bridge it to `NSError` when needed.

## How errors are delivered

You never construct a `PrimerError` yourself. The SDK delivers it to you through one of two channels, depending on how you present checkout.

| Channel | Where the error arrives |
| - | - |
| SwiftUI state | [`PrimerCheckoutState.failure(PrimerError, checkoutData:)`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-state), delivered to the completion closure of the `.primerCheckoutSession(session) { state in ... }` modifier (or `PrimerCheckout(clientToken:) { state in ... }`). Switch on `.failure`. Once checkout is ready, `.failure` arrives once per failed payment attempt and the checkout stays open so the shopper can retry. `.success` or `.dismissed` follows exactly once. A failure while the session is still initializing, before the checkout is ready, ends the `.primerCheckoutSession` session, and nothing more is delivered. `PrimerCheckout(clientToken:)` also reports a failed initialization through the closure while it shows its error screen with a retry. |
| Presenter delegate | [`PrimerCheckoutPresenterDelegate`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-presenter) `primerCheckoutPresenterDidFailWithError(_:checkoutData:)` for UIKit / presenter-driven integrations. It is called once per failed attempt, including a failed initialization. While the SDK error screen is enabled, the sheet stays open for a retry, and `primerCheckoutPresenterDidCompleteWithSuccess(_:)` or `primerCheckoutPresenterDidDismiss()` follows exactly once. When `isErrorScreenEnabled` is `false`, the sheet closes on the failure. |

See [Handle the payment result](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/guides/handle-payment-result.md) for the full success / failure / dismissed flow.

## Properties

| Property | Type | Description |
| - | - | - |
| `errorId` | `String` | Stable, kebab-case identifier for the error category (for example `"invalid-client-token"`, `"payment-cancelled"`, `"apple-pay-no-cards-in-wallet"`). Use this for programmatic branching. |
| `diagnosticsId` | `String` | Unique per-occurrence diagnostics ID. Log it and provide it to Primer support when investigating an issue. |
| `recoverySuggestion` | `String?` | Suggested recovery action when one applies (for example "Check if the token you have provided is a valid token"). `nil` when no recovery is available (for example a user-cancelled payment). |
| `plainDescription` | `String?` | The error message without the `errorId` and `diagnosticsId` wrapper. This is the text the SDK's default error screen shows. |
| `errorDescription` | `String?` | Message for logging: `"[<errorId>] <plainDescription> (diagnosticsId: <diagnosticsId>)"`. Also surfaced as `localizedDescription` via `LocalizedError`. |
| `errorUserInfo` | `[String: Any]` | `NSError` user-info dictionary. Includes `"diagnosticsId"` and a `"createdAt"` timestamp. |
| `underlyingErrorCode` | `String?` | Reserved for NOL Pay provider errors, which CheckoutComponents does not produce. Always `nil` for errors delivered by CheckoutComponents. |

> [!WARNING]
>
> `errorDescription` and `localizedDescription` include the error and diagnostics IDs and are meant for logs. The SDK's default error screen shows `plainDescription`. In your own UI, show your own localized, branded message.

## Handling an error

Receive the checkout state through the completion closure and, in the `.failure` branch, log the `diagnosticsId` and `errorId`. Branch on `errorId` only when you need behavior specific to a category (for example `"payment-failed"` for a declined payment versus a configuration error such as `"invalid-client-token"`).

> [!NOTE]
>
> A shopper who cancels a payment method (closing the Apple Pay sheet or PayPal, leaving a redirect) is not a failure. Tapping PayPal's "Cancel and return" link is also a cancel. For Klarna, closing the consent alert or the web sheet is a cancel, and so is a decline from Klarna. CheckoutComponents returns to the payment method list, or delivers `.dismissed` when there is no list to return to. Klarna SDK errors (`"klarna-sdk-error"`) reach `.failure`. `"payment-cancelled"` reaches `.failure` only in rare edge cases.

```swift theme={"dark"}
import SwiftUI
import PrimerSDK

struct CheckoutScreen: View {
    @StateObject private var session: PrimerCheckoutSession

    init(clientToken: String) {
        _session = StateObject(wrappedValue: PrimerCheckoutSession(clientToken: clientToken))
    }

    var body: some View {
        ScrollView {
            PrimerCardForm()
        }
        .primerCheckoutSession(session) { state in
            handle(state)
        }
    }

    private func handle(_ state: PrimerCheckoutState) {
        switch state {
        case let .failure(error, _):
            handle(error)
        case let .success(result):
            print("Payment completed: \(result.paymentId)")
        case .dismissed, .initializing, .ready:
            break
        @unknown default:
            break
        }
    }

    private func handle(_ error: PrimerError) {
        // Always log the diagnostics ID — share it with Primer support.
        print("Checkout failed [\(error.errorId)]: \(error.localizedDescription)")
        print("Diagnostics ID: \(error.diagnosticsId)")

        switch error.errorId {
        case "payment-cancelled":
            // Rarely delivered here: the shopper backed out, so no message is needed.
            break
        default:
            // Show your own branded, localized error message here.
            showRetryAlert(suggestion: error.recoverySuggestion)
        }
    }
}
```

For a UIKit / presenter integration, the same `PrimerError` arrives through the delegate:

```swift theme={"dark"}
extension CheckoutCoordinator: PrimerCheckoutPresenterDelegate {
    func primerCheckoutPresenterDidFailWithError(_ error: PrimerError, checkoutData: PrimerCheckoutData?) {
        print("Checkout failed [\(error.errorId)] — diagnosticsId: \(error.diagnosticsId)")
        // Present your own error UI; use error.recoverySuggestion to decide whether to offer a retry.
    }
}
```

> [!TIP]
>
> When filing a support ticket, include the `diagnosticsId`. It lets Primer trace the exact occurrence of the failure across SDK and backend logs.

## See also

> [card] [PrimerCheckoutState](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-state.md)
>
> The checkout state that delivers `.failure(PrimerError, checkoutData:)`.

> [card] [Handle the payment result](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/guides/handle-payment-result.md)
>
> Handle success, failure, and dismissal end to end.

> [card] [PrimerCheckoutPresenter](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-presenter.md)
>
> Receive failures through the presenter delegate.

> [card] [ValidationResult](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/common/validation-result.md)
>
> Inline field validation, distinct from checkout errors.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.