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

# Error Handling

> Handle SDK errors with structured error codes and recovery patterns

# Error Handling

The Privacy Boost SDK uses structured error codes so you can handle errors programmatically. Every error has a stable `code` string that won't change between SDK versions.

## Quick Example

```typescript theme={null}
import { PrivacyBoostError, ErrorCodes } from '@sunnyside-io/privacy-boost';

try {
  await sdk.vault.shield({ tokenAddress, amount });
} catch (error) {
  if (error instanceof PrivacyBoostError) {
    switch (error.code) {
      case ErrorCodes.TRANSACTION_REJECTED:
        // User cancelled — do nothing
        return;
      case ErrorCodes.INSUFFICIENT_BALANCE:
        showToast('Not enough balance');
        return;
      default:
        if (error.retryable) {
          // Network issue — retry after a moment
          await retryLater();
        } else {
          showError(error.message);
        }
    }
  }
}
```

## Error Types by Platform

| Platform         | Error Type          | How to check the code                                  |
| ---------------- | ------------------- | ------------------------------------------------------ |
| TypeScript       | `PrivacyBoostError` | `error.code`, `error.retryable`, `error.isAuthError()` |
| iOS (Swift)      | `SDKError`          | `catch SDKError.networkError(let msg) { ... }`         |
| Android (Kotlin) | `SDKError`          | `catch (e: SDKError.NetworkError) { ... }`             |

### TypeScript: PrivacyBoostError

```typescript theme={null}
class PrivacyBoostError extends Error {
  code: string;          // e.g. 'WALLET_NOT_CONNECTED'
  message: string;       // Human-readable description
  cause?: Error;         // Original error if wrapped
  retryable: boolean;    // Can this operation be retried?

  isAuthError(): boolean;  // Is this an auth/session error?
  isWalletError(): boolean; // Is this a wallet error?
}
```

## Error Codes

### Wallet Errors

These occur when interacting with the user's wallet.

| Code                       | What happened                            | User action                     |
| -------------------------- | ---------------------------------------- | ------------------------------- |
| `WALLET_NOT_CONNECTED`     | No wallet connected                      | Show "Connect Wallet" button    |
| `WALLET_CONNECTION_FAILED` | Wallet refused or failed to connect      | Retry or try a different wallet |
| `WRONG_NETWORK`            | Wallet is on the wrong chain             | Show "Switch Network" prompt    |
| `TRANSACTION_REJECTED`     | User rejected a signature or transaction | Do nothing (intentional)        |
| `TRANSACTION_FAILED`       | On-chain transaction failed              | Show error, check gas/params    |
| `INSUFFICIENT_GAS`         | Not enough gas for the transaction       | Prompt user to add funds        |

### Authentication Errors

These occur during login or when a session expires.

| Code                    | What happened                      | User action                        |
| ----------------------- | ---------------------------------- | ---------------------------------- |
| `NOT_AUTHENTICATED`     | Tried to use SDK before logging in | Call `authenticate()` first        |
| `SESSION_EXPIRED`       | JWT expired                        | Re-authenticate (can be automatic) |
| `AUTHENTICATION_FAILED` | Login failed                       | Retry or check configuration       |

### Network Errors

These are transient and usually retryable.

| Code                  | What happened                             | Retryable?         |
| --------------------- | ----------------------------------------- | ------------------ |
| `NETWORK_ERROR`       | Connection failed (DNS, timeout, refused) | Yes                |
| `TIMEOUT`             | Request took too long                     | Yes                |
| `RATE_LIMITED`        | Too many requests (429)                   | Yes (with backoff) |
| `SERVICE_UNAVAILABLE` | Backend is temporarily down               | Yes (with backoff) |

### Operation Errors

These occur during deposits, transfers, or withdrawals.

| Code                   | What happened                          | User action                                         |
| ---------------------- | -------------------------------------- | --------------------------------------------------- |
| `INSUFFICIENT_BALANCE` | Not enough shielded balance            | Show balance and suggest depositing more            |
| `INVALID_ADDRESS`      | Malformed Ethereum or privacy address  | Prompt user to check the address                    |
| `INVALID_AMOUNT`       | Amount is zero, negative, or malformed | Validate input before calling the SDK               |
| `INVALID_CONFIG`       | SDK configuration is invalid           | Check `appId`, `serverUrl`, and other config values |

### Compliance Errors

These occur when a transaction fails compliance screening.

| Code                         | What happened                                          | User action     |
| ---------------------------- | ------------------------------------------------------ | --------------- |
| `COMPLIANCE_BLOCKED`         | Address or transaction blocked by compliance screening | Contact support |
| `TRANSFER_COMPLIANCE_FAILED` | Transfer failed compliance check                       | Contact support |

### Crypto Errors

These indicate failures in the cryptographic layer.

| Code                   | What happened                                    | User action                     |
| ---------------------- | ------------------------------------------------ | ------------------------------- |
| `CRYPTO_ERROR`         | General cryptographic operation failed           | Report the issue                |
| `ENCRYPTION_ERROR`     | Failed to encrypt data                           | Retry or re-authenticate        |
| `DECRYPTION_ERROR`     | Failed to decrypt data (wrong keys or corrupted) | Re-authenticate or restore keys |
| `KEY_DERIVATION_ERROR` | Failed to derive privacy keys                    | Re-authenticate                 |

### Shield Errors

These occur during shield (deposit) operations.

| Code                          | What happened                     | User action              |
| ----------------------------- | --------------------------------- | ------------------------ |
| `SHIELD_NOT_FOUND`            | Shield request not found on-chain | Check transaction hash   |
| `SHIELD_ALREADY_CLAIMED`      | Shield has already been claimed   | No action needed         |
| `SHIELD_PROOF_EXPIRED`        | Shield proof has expired          | Re-initiate the shield   |
| `SHIELD_COMMITMENT_NOT_FOUND` | On-chain commitment not found     | Wait for indexer to sync |

### Transfer Errors

These occur during private transfers.

| Code                         | What happened                                 | User action        |
| ---------------------------- | --------------------------------------------- | ------------------ |
| `TRANSFER_NOT_FOUND`         | Transfer not found                            | Check request ID   |
| `TRANSFER_PROOF_FAILED`      | ZK proof generation or verification failed    | Retry the transfer |
| `TRANSFER_SUBMISSION_FAILED` | Transfer could not be submitted to the server | Retry              |
| `BELOW_MIN_TRANSFER_AMOUNT`  | Amount is below minimum transfer threshold    | Increase amount    |

### Merkle Tree Errors

These occur during proof construction.

| Code                    | What happened                            | Retryable?                   |
| ----------------------- | ---------------------------------------- | ---------------------------- |
| `MERKLE_LEAF_NOT_FOUND` | Note's leaf not found in the Merkle tree | Yes (indexer may be syncing) |
| `MERKLE_PROOF_FAILED`   | Failed to construct Merkle proof         | Yes                          |
| `MERKLE_ROOT_STALE`     | Merkle root is outdated                  | Yes (auto-retried by SDK)    |

### Note Errors

These occur when accessing private notes.

| Code                 | What happened                                  | User action               |
| -------------------- | ---------------------------------------------- | ------------------------- |
| `NOTE_NOT_FOUND`     | Note does not exist                            | Verify the transaction    |
| `NOTE_ALREADY_SPENT` | Note has already been spent (nullifier exists) | No action — note was used |
| `NOTE_ACCESS_DENIED` | Cannot decrypt this note (wrong keys)          | Verify correct account    |

### Internal Errors

These indicate SDK bugs or unexpected server responses.

| Code                  | What happened                        | User action                              |
| --------------------- | ------------------------------------ | ---------------------------------------- |
| `SERIALIZATION_ERROR` | Failed to serialize/deserialize data | Report the issue                         |
| `OPERATION_FAILED`    | Generic operation failure            | Check the error message for details      |
| `INTERNAL_ERROR`      | Unexpected internal error            | Report the issue with full error details |

## Recovery Patterns

### Handle user rejections gracefully

When a user cancels a wallet popup, don't show an error dialog — it's intentional.

```typescript theme={null}
try {
  await sdk.auth.authenticate(adapter, { keySource: { type: 'walletDerived' } });
} catch (error) {
  if (error.code === 'TRANSACTION_REJECTED') {
    return; // User cancelled
  }
  showError(error.message);
}
```

### Retry on network errors

Use `error.retryable` with exponential backoff:

```typescript theme={null}
async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (!error.retryable || attempt === maxRetries) throw error;
      await new Promise(r => setTimeout(r, 1000 * attempt));
    }
  }
  throw new Error('Unreachable');
}

const balance = await withRetry(() => sdk.vault.getBalance(token));
```

### Re-authenticate on session expiry

```typescript theme={null}
async function withAuth<T>(fn: () => Promise<T>): Promise<T> {
  try {
    return await fn();
  } catch (error) {
    if (error.code === 'SESSION_EXPIRED') {
      await sdk.auth.authenticate(adapter, { keySource: { type: 'walletDerived' } });
      return await fn();
    }
    throw error;
  }
}
```

### Map errors to user-friendly messages

```typescript theme={null}
function userMessage(error: PrivacyBoostError): string {
  const messages: Record<string, string> = {
    WALLET_NOT_CONNECTED: 'Please connect your wallet',
    TRANSACTION_REJECTED: 'Request cancelled',
    WRONG_NETWORK: 'Please switch to the correct network',
    SESSION_EXPIRED: 'Session expired, please log in again',
    INSUFFICIENT_BALANCE: 'Not enough balance',
    NETWORK_ERROR: 'Connection failed, please try again',
    RATE_LIMITED: 'Too many requests, please wait',
  };

  return messages[error.code] || error.message;
}
```

## Best Practices

1. **Match on `error.code`, not `error.message`.** Codes are stable across versions; messages may change.

2. **Never show raw error messages to users.** Map codes to context-appropriate messages.

3. **Handle `TRANSACTION_REJECTED` silently.** The user intentionally cancelled.

4. **Use `error.retryable` for retry logic.** The SDK marks transient errors as retryable.

5. **Log the full error internally.** Capture `code`, `message`, and `cause` for debugging, even when showing a simple message to the user.

## Next Steps

You've completed the Setup section. Now dive into your platform SDK:

<CardGroup cols={3}>
  <Card title="TypeScript" href="/sdk/typescript/getting-started">
    Full web SDK guide
  </Card>

  <Card title="React" href="/sdk/react/getting-started">
    Hooks and components
  </Card>

  <Card title="iOS" href="/sdk/ios/getting-started">
    Native Swift SDK
  </Card>

  <Card title="Android" href="/sdk/android/getting-started">
    Native Kotlin SDK
  </Card>

  <Card title="React Native" href="/sdk/react-native/getting-started">
    React Native SDK
  </Card>

  <Card title="CLI" href="/sdk/cli/getting-started">
    Command-line tool
  </Card>
</CardGroup>

For deeper TypeScript error handling patterns (retry logic, WASM loading, caching), see the [TypeScript Error Handling guide](/sdk/typescript/guides/error-handling).
