> ## 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.

# Angular SDK - Error Handling

> Error hierarchy, APP_INITIALIZER failure handling, and quarantine in the Angular SDK.

The SDK distinguishes between two classes of failures:

1. **Initial sync failures** - surfaced as exceptions during `APP_INITIALIZER`. The Angular bootstrap fails, which is the correct behaviour for a configuration source.
2. **Background sync / SSE failures** - never thrown. They are logged via `NexusLogger.error(...)`, and the existing cache continues to be served.

## Error hierarchy

All SDK errors extend `NexusError`. A single `instanceof` check covers everything:

```ts theme={null}
import { NexusError } from '@westyx-nexus/sdk-angular';

try {
  const banner = nexus.getConfig('public.banner');
} catch (err) {
  if (err instanceof NexusError) {
    // SDK-specific failure - handle as needed
  }
}
```

The full hierarchy:

```
Error
└── NexusError                          (base class)
    ├── NexusInitError                  (initial sync failure - cause has detail)
    ├── NexusUnauthorizedError          (HTTP 401)
    ├── NexusBillingError               (HTTP 402 - overdue invoices, v0.2.0)
    ├── NexusServiceKindMismatchError   (getSecret on kind=frontend, v0.2.0)
    ├── NexusAbAddonNotAvailableError   (evaluateAB on tenant without AB add-on, v0.3.0)
    ├── NexusServerError                (HTTP 5xx on /sync, caught internally by the retry loop, v0.3.1)
    ├── NexusQuarantinedError           (HTTP 429 quarantined - reason + expiresAt, v0.4.0)
    ├── NexusNotFoundError              (HTTP 404)
    ├── NexusPublicKeyError             (getSecret called with public key)
    └── NexusSecretNotFoundError        (key absent from cache)
```

## When each is thrown

| Error                           | Thrown by                                                     | When                                                                                                                                                                                               |
| ------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NexusInitError`                | `provideNexus()` / `NexusModule.forRoot()` (during bootstrap) | The initial sync failed for any reason. The `cause` property carries the underlying error.                                                                                                         |
| `NexusUnauthorizedError`        | Initial sync, `nexus.sync()`                                  | Server returned HTTP 401 - invalid API key, slug/key mismatch, or revoked key.                                                                                                                     |
| `NexusBillingError`             | Initial sync, `nexus.sync()`                                  | Server returned HTTP 402 - the tenant has overdue invoices. The SDK halts the background refresh loop. Cleared on the next successful sync.                                                        |
| `NexusServiceKindMismatchError` | `getSecret()`                                                 | The service's `kind` is `"frontend"`. Frontend services are public by design and must not hold secrets.                                                                                            |
| `NexusAbAddonNotAvailableError` | `evaluateAB()`, `evaluateABPromise()`                         | Server returned HTTP 403 - the AB Testing add-on is not active for the tenant.                                                                                                                     |
| `NexusServerError`              | `nexus.sync()` (caught internally, v0.3.1)                    | Server returned HTTP 5xx on `/sync`. The SDK catches this and schedules an exponential-backoff retry. Exported for `instanceof` checks in custom logging interceptors.                             |
| `NexusQuarantinedError`         | `nexus.sync()` (v0.4.0)                                       | Server returned HTTP 429 with `{"error":"quarantined"}`. Carries `reason: string` and `expiresAt: Date`. The SDK suspends sync until `expiresAt` and fires `onQuarantined` on the stream observer. |
| `NexusNotFoundError`            | Initial sync, `nexus.sync()`, `evaluateAB()`                  | Server returned HTTP 404 - unknown subdomain or missing endpoint.                                                                                                                                  |
| `NexusPublicKeyError`           | `nexus.getSecret()`                                           | The client uses a public key. Thrown synchronously. Browser SDKs always throw this for `getSecret`.                                                                                                |
| `NexusSecretNotFoundError`      | `nexus.getSecret()`                                           | Practically never reached in browsers, since `NexusPublicKeyError` fires first.                                                                                                                    |

## Catching during an `APP_INITIALIZER` failure

The default behaviour is to fail the bootstrap. To recover gracefully (e.g. show a degraded UI), you can wrap the bootstrap:

```ts theme={null}
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { NexusInitError } from '@westyx-nexus/sdk-angular';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';

bootstrapApplication(AppComponent, appConfig).catch((err) => {
  if (err instanceof NexusInitError) {
    document.body.innerHTML = '<h1>Service temporarily unavailable</h1>';
  } else {
    throw err;
  }
});
```

## Quarantine handling (v0.4.0+)

When the Nexus backend places a service in quarantine it responds HTTP 429 with a JSON body containing `"error":"quarantined"`, a `"reason"` string, and an ISO-8601 `"expires_at"` timestamp. The SDK:

1. Throws `NexusQuarantinedError` (carrying `reason` and `expiresAt`) from `sync()`.
2. Fires `onQuarantined(reason, expiresAt)` on the stream observer (if set).
3. Suspends `ensureFresh` background syncing until `expiresAt`.
4. On the stream pre-flight, schedules a reconnect after the quarantine window (minimum 5 s) without incrementing the failure counter.

```ts theme={null}
import { NexusQuarantinedError } from '@westyx-nexus/sdk-angular';

try {
  await nexus.sync();
} catch (err) {
  if (err instanceof NexusQuarantinedError) {
    console.warn(`Quarantined: ${err.reason} until ${err.expiresAt.toISOString()}`);
  }
}
```

## 5xx exponential backoff (v0.3.1+)

When the server returns a `5xx` status on `/sync`, the SDK:

1. Catches the response internally and surfaces it as a `NexusServerError(status, bodySnippet)`.

2. Increments an internal attempt counter and schedules a retry via `setTimeout` with **exponential backoff**:

   | Attempt | Delay         |
   | ------- | ------------- |
   | 1       | 1 s           |
   | 2       | 2 s           |
   | 3       | 4 s           |
   | 4       | 8 s           |
   | 5       | 16 s          |
   | 6+      | 30 s (capped) |

3. Emits `FALLBACK_REASON_TRANSPORT` on the stream observer during the wait.

4. On the next successful sync - including the auto-retry succeeding - resets the counter.

```ts theme={null}
import { provideNexus, FALLBACK_REASON_TRANSPORT } from '@westyx-nexus/sdk-angular';

provideNexus({
  baseUrl: '...',
  apiKey: '...',
  streamObserver: {
    onFallback(reason) {
      if (reason === FALLBACK_REASON_TRANSPORT) {
        // 5xx wait - surface "syncing..." in the UI
      }
    },
  },
});
```
