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

# Error Handling

> Handle SDK errors gracefully in your app

All SDK operations throw `GrantivaError`, which conforms to `LocalizedError`.

## Error types

| Error                                              | When                                                                                                                                                                        | Suggested action                                                                                                                                                                                             |
| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `deviceNotSupported`                               | Physical device doesn't support App Attest (old hardware or restricted region)                                                                                              | Degrade gracefully; this is a permanent device limitation                                                                                                                                                    |
| `simulatorAPIKeyRequired`                          | `validateAttestation()` called in the iOS Simulator without an API key configured                                                                                           | Initialize with `Grantiva(teamId:apiKey:)` — see [Simulator Setup](/simulator-setup)                                                                                                                         |
| `attestationNotAvailable`                          | App Attest not supported in this region                                                                                                                                     | Degrade gracefully                                                                                                                                                                                           |
| `networkError(Error)`                              | Network communication failed                                                                                                                                                | Retry with backoff                                                                                                                                                                                           |
| `validationFailed`                                 | Server rejected the attestation                                                                                                                                             | Device may be compromised — block or challenge                                                                                                                                                               |
| `tokenExpired`                                     | JWT token has expired                                                                                                                                                       | Call `refreshToken()` or `validateAttestation()`                                                                                                                                                             |
| `configurationError`                               | Invalid Bundle ID or Team ID                                                                                                                                                | Check SDK initialization                                                                                                                                                                                     |
| `keyGenerationFailed`                              | Can't create attestation key                                                                                                                                                | Retry; may indicate Keychain issue                                                                                                                                                                           |
| `challengeExpired`                                 | Server challenge timed out                                                                                                                                                  | Retry — the SDK handles this automatically                                                                                                                                                                   |
| `invalidResponse`                                  | Unexpected server response format                                                                                                                                           | Check SDK/server version compatibility                                                                                                                                                                       |
| `rateLimited`                                      | Too many requests                                                                                                                                                           | Back off and retry after delay                                                                                                                                                                               |
| `feedbackNotAvailable`                             | Feedback not enabled for this tenant                                                                                                                                        | Check your plan includes feedback                                                                                                                                                                            |
| `reattestRequired`                                 | Server reports attestation key has drifted (rpIdHash or signature mismatch)                                                                                                 | The SDK self-heals: it clears the cached keyId and re-runs attestation automatically. Avoid wrapping SDK calls in retry loops that re-call `validateAttestation()` — the SDK already retries once internally |
| `serverError(reason: String)`                      | Server-side validation failure with a developer-readable reason                                                                                                             | Log `error.reason` for diagnostics; do not show the raw reason to end users                                                                                                                                  |
| `keyAlreadyAttested`                               | Apple's DCAppAttestService rejected `attestKey` because the keyId was already attested in a prior session                                                                   | The SDK self-heals by clearing the stored keyId and generating a fresh one. If the retry also fails, this error surfaces to the caller — typically after a re-install or Keychain wipe                       |
| `assertionKeyInvalid` (2.0.5+)                     | Apple rejected `generateAssertion()` because the stored keyId references a key that can no longer produce assertions (backup restore, device transfer, drifted local state) | The SDK self-heals: it clears the stored keyId and tokens and runs one full re-attestation. Surfaces only if that also fails                                                                                 |
| `limitExceeded(limit: Int, current: Int)` (2.1.0+) | Your organization's Monthly Active Device limit was reached and the grace period has expired (HTTP 402 from the server)                                                     | Existing devices keep working; only new devices hit this. Upgrade your plan, or degrade gracefully on-device                                                                                                 |

## Example

```swift theme={null}
do {
    let result = try await grantiva.validateAttestation()
} catch let error as GrantivaError {
    switch error {
    case .simulatorAPIKeyRequired:
        // Initialize with Grantiva(teamId:apiKey:) for Simulator builds
        print("Configure an API key for Simulator builds — see Simulator Setup")
    case .deviceNotSupported:
        // Physical device doesn't support App Attest (old hardware / restricted region)
        print("App Attest not available on this device")
    case .networkError(let underlying):
        print("Network error: \(underlying.localizedDescription)")
    case .validationFailed:
        print("Device verification failed")
    case .tokenExpired:
        let refreshed = try await grantiva.refreshToken()
    case .serverError(let reason):
        // Log reason for diagnostics
        print("Server error: \(reason)")
    case .rateLimited:
        try await Task.sleep(for: .seconds(5))
    default:
        print(error.localizedDescription)
    }
}
```

## Localized descriptions

Every `GrantivaError` case provides a human-readable `errorDescription`:

```swift theme={null}
GrantivaError.simulatorAPIKeyRequired.errorDescription
// "An API key is required to use Grantiva in the iOS Simulator"

GrantivaError.deviceNotSupported.errorDescription
// "This device does not support App Attest functionality"

GrantivaError.rateLimited.errorDescription
// "You have exceeded the rate limit for this action"

GrantivaError.serverError(reason: "invalid_key").errorDescription
// "Server error: invalid_key"
```
