> For the complete documentation index, see [llms.txt](https://docs.ecommerce.seb.ee/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ecommerce.seb.ee/integration/mobile-sdk/ios-sdk/guides/error-handling.md).

# Error handling

Use this guide if you want to turn an SDK error into something actionable.

## How errors reach you

Errors arrive as `NSError` on `PaymentResult.error`, never as a thrown exception, and always alongside an outcome. The SDK does not throw and does not require a `try`.

An outcome of `.failed` with a `nil` error means the payment itself did not succeed. An outcome carrying an error means the SDK could not complete the flow.

All errors use the domain:

```
com.egw.egwpay
```

***

## Error codes

| Code | Name                       | Meaning                                                                                      |
| ---- | -------------------------- | -------------------------------------------------------------------------------------------- |
| 1    | `invalidRequest`           | The request is not usable, or a payment is already running on this `PaymentKit`.             |
| 2    | `statusLookupFailed`       | The payment status could not be reached. `NSUnderlyingErrorKey` carries the transport error. |
| 3    | `statusLookupUnavailable`  | No `apiBaseURL` is configured and no custom resolver was supplied.                           |
| 4    | `httpError`                | The status lookup returned a non-2xx response. `EGWHTTPStatusCode` carries the status code.  |
| 5    | `malformedResponse`        | The status response could not be read.                                                       |
| 6    | `webViewLoadFailed`        | The checkout page could not be loaded. `NSUnderlyingErrorKey` carries the cause.             |
| 7    | `cardValidationFailed`     | A card field failed validation before any network call was made.                             |
| 8    | `encryptionKeyUnavailable` | No `checkoutBaseURL` is configured, or the card encryption key could not be fetched.         |
| 9    | `encryptionFailed`         | The card details could not be encrypted.                                                     |
| 10   | `cardSessionFailed`        | The card session could not be created or advanced. `NSUnderlyingErrorKey` carries the cause. |
| 11   | `cardActionUnsupported`    | The card session asked for a step that cannot be completed in the app.                       |
| 12   | `threeDSChallengeFailed`   | The 3-D Secure challenge could not be completed.                                             |

Codes 1 to 6 can occur in either flow. Codes 7 to 12 occur only in the native card form.

Every error carries `NSLocalizedDescriptionKey`. Read `localizedDescription` for a message that already names the failing field or the HTTP status, and match on `code` when you need to branch.

```swift
if let error = result.error, error.domain == PaymentError.domain {
    switch PaymentError.Code(rawValue: error.code) {
    case .httpError:
        let status = error.userInfo["EGWHTTPStatusCode"] as? Int
        // …
    case .cardValidationFailed:
        // …
    default:
        break
    }
}
```

***

## Common failures

### Card brand not accepted

**Cause** - The shop's configuration does not accept the brand of the card the shopper entered. The gateway answers with HTTP 403.

**Resolution** - Show a "this card type is not accepted" message and let the shopper try another card. Do not add a client-side brand allowlist to your app: a shop's accepted methods can change without your app being rebuilt, and a local list would go stale.

### Payment status could not be confirmed

**Cause** - Code 2, 3, 4 or 5. The SDK either had nowhere to ask, or asked and did not get a usable answer. The payment itself may well have succeeded.

**Resolution** - Treat the payment as still open. Show a confirming state and ask your own backend using `result.paymentId`. Never show it as a failure, and never let the shopper pay twice on the strength of it.

### The checkout page could not load

**Cause** - Code 6. The web view could not reach the checkout host. `NSUnderlyingErrorKey` carries the underlying `NSURLError`.

**Resolution** - Check the checkout URL you passed and the device's connectivity, then offer a retry. The payment is untouched, so the same `paymentId` and `verificationKey` can be used again.

### A card field failed validation

**Cause** - Code 7. One of the card fields did not pass validation. The message names it.

**Resolution** - Validate as the shopper types with `card.validationError(for:)` so the failure surfaces in your form instead of at submission. See [Native card form](/integration/mobile-sdk/ios-sdk/using-the-sdk/native-card-form.md).

### A payment is already running

**Cause** - Code 1. `startPayment` or `startCardPayment` was called while the same `PaymentKit` still had a payment on screen.

**Resolution** - Disable your pay button for the duration of a payment, or create a `PaymentKit` per payment. The first payment is unaffected.

***

## Tracing a failure

Enable logging when you need to follow a flow:

```swift
let configuration = Configuration(
    apiBaseURL: URL(string: "https://your-backend.example.com"),
    environment: .sandbox,
    loggingEnabled: true
)
```

The SDK logs flow milestones through `os_log`. It never logs request or response bodies, card data, credentials, or full URLs.

To correlate a failure end to end, use the `X-Request-Id` your backend sent when it created the payment, together with the `paymentId` on the result. The SDK's own calls carry a request id as well.

Where the gateway supplied detail, it arrives under `NSUnderlyingErrorKey`, and the HTTP status of a failed status lookup arrives under `EGWHTTPStatusCode`.

***

## Retries

The SDK does not retry anything. A failed status lookup is reported, not repeated.

{% hint style="warning" %}
⚠️ **Do not start a second payment after an unclear failure.** Codes 2, 4, 5 and 10 all leave the real state of the payment unknown, and creating a new payment risks charging the shopper twice. Query the state from your backend first, and only start again if the payment demonstrably did not take effect.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.ecommerce.seb.ee/integration/mobile-sdk/ios-sdk/guides/error-handling.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
