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

# Kotlin SDK - Error Handling

> Exception table and catch examples for the Kotlin SDK.

All SDK exceptions extend `NexusException`, which extends `RuntimeException`. You can catch the base class to handle all SDK errors in one place, or catch specific subclasses for fine-grained control.

## Exception reference

| Exception                              | When it is thrown                                                                                                                                | Thrown by                                          |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------- |
| `NexusException`                       | Base class for all SDK errors. Also thrown directly for unexpected HTTP status codes.                                                            | Various                                            |
| `NexusInitException`                   | Initial sync failed at client creation. Wraps the underlying cause.                                                                              | `NexusClient.create`                               |
| `NexusUnauthorizedException`           | HTTP 401 - invalid API key, endpoint mismatch, or expired session that could not be refreshed.                                                   | `sync`, `NexusClient.create`                       |
| `NexusNotFoundException`               | HTTP 404 - the service was not found. Usually indicates a wrong `baseUrl`.                                                                       | `sync`, `NexusClient.create`                       |
| `NexusPublicKeyException`              | A `wxp_...` key was used to call `getSecret` or `getSecretFilePath`. Public keys cannot access secrets.                                          | `getSecret`, `getSecretFilePath`                   |
| `NexusSecretNotFoundException`         | The requested secret key is not in the current snapshot.                                                                                         | `getSecret`, `getSecretFilePath`                   |
| `NexusBillingException`                | HTTP 402 - the tenant has overdue invoices. Sync is paused; `observer.onBillingOverdue()` is also called.                                        | `sync`                                             |
| `NexusServiceKindMismatchException`    | `getSecret` was called on a `frontend` service, which never syncs secrets.                                                                       | `getSecret`                                        |
| `NexusWifNotConfiguredException`       | WIF is enabled but no token could be resolved for the current environment.                                                                       | `NexusClient.create` (during session init)         |
| `NexusWifTokenExchangeFailedException` | The OIDC token was obtained but the `POST /v1/auth/token-exchange` request failed.                                                               | `NexusClient.create`, background session refresh   |
| `NexusSessionExpiredException`         | WIF session expired and the background refresh failed.                                                                                           | Background session refresh                         |
| `NexusAbAddonNotAvailableException`    | HTTP 403 from `evaluateAB` - the A/B add-on is not active for this project.                                                                      | `evaluateAB`                                       |
| `NexusQuarantinedException`            | HTTP 429 with a quarantine payload - the service is temporarily banned from calling the API. Contains `reason: String` and `expiresAt: Instant`. | `sync`                                             |
| `NexusRateLimitedException`            | HTTP 429 on write endpoints - contains `retryAfter: Duration` for the recommended retry delay.                                                   | `setSecret`, `deleteSecret`, `deleteSecretVersion` |

## Catching exceptions at client creation

```kotlin theme={null}
val client = try {
    NexusClient.create(config)
} catch (e: NexusInitException) {
    // initial sync failed - could be network, DNS, or server error
    logger.error("Could not connect to Nexus", e)
    exitProcess(1)
} catch (e: NexusUnauthorizedException) {
    // bad API key or wrong baseUrl
    logger.error("Nexus auth failed - check NEXUS_API_KEY and baseUrl")
    exitProcess(1)
} catch (e: NexusNotFoundException) {
    // baseUrl points to a non-existent service
    logger.error("Nexus service not found - check baseUrl")
    exitProcess(1)
} catch (e: NexusBillingException) {
    // tenant billing overdue
    logger.warn("Nexus: billing overdue, running on last cached values")
    // continue with empty cache or exit depending on your policy
}
```

## Catching secret access errors

```kotlin theme={null}
val secret = try {
    client.getSecret("stripe_secret_key")
} catch (e: NexusPublicKeyException) {
    // public key used - this is a programming error
    throw IllegalStateException("Use a secret key for backend services", e)
} catch (e: NexusServiceKindMismatchException) {
    // frontend service - secrets not available
    throw IllegalStateException("Frontend services cannot access secrets", e)
} catch (e: NexusSecretNotFoundException) {
    // key does not exist in the current snapshot
    logger.warn("Secret 'stripe_secret_key' not found, using env fallback")
    System.getenv("STRIPE_SECRET_KEY") ?: error("No fallback available")
}
```

## Catching A/B errors

```kotlin theme={null}
val results = try {
    client.evaluateAB(listOf("checkout_v2"), userId = "user-123")
} catch (e: NexusAbAddonNotAvailableException) {
    // add-on not active - fall back to default
    mapOf("checkout_v2" to false)
} catch (e: NexusException) {
    // any other SDK error
    logger.warn("evaluateAB failed: ${e.message}")
    mapOf("checkout_v2" to false)
}
```

## WIF-specific errors

```kotlin theme={null}
val config = NexusConfig(
    baseUrl = "https://my-service.westyx.dev",
    wif = WifConfig(enabled = true, provider = "kubernetes"),
)

val client = try {
    NexusClient.create(config)
} catch (e: NexusWifNotConfiguredException) {
    // Kubernetes service account token not found at expected path
    logger.error("WIF token not available: ${e.message}")
    exitProcess(1)
} catch (e: NexusWifTokenExchangeFailedException) {
    // Token obtained but exchange endpoint rejected it
    logger.error("WIF token exchange failed: ${e.message}")
    exitProcess(1)
} catch (e: NexusInitException) {
    logger.error("Nexus init failed", e.cause)
    exitProcess(1)
}
```

## Quarantine

When the SDK receives a quarantine response (HTTP 429 with a JSON body containing `"error": "quarantined"`), it throws `NexusQuarantinedException` from `sync()` and also calls `observer.onQuarantined(reason, expiresAt)`. For the SSE stream, the quarantine is handled automatically - the stream sleeps until `expiresAt` and reconnects without incrementing the failure counter.

```kotlin theme={null}
try {
    client.sync()
} catch (e: NexusQuarantinedException) {
    println("Quarantined: ${e.reason}, lifts at ${e.expiresAt}")
    // TTL background sync will also pause until expiresAt
}
```
