> ## Documentation Index
> Fetch the complete documentation index at: https://rain-sandbox-trial.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Reference

> Complete reference for Rain Client SDK error codes on Android and iOS, with handling patterns and troubleshooting guidance.

Both SDKs use standardized error codes prefixed with `RAIN_`. iOS throws `RainSDKError` (a Swift enum conforming to `LocalizedError`); Android throws `RainError` (a sealed class). Every error exposes an error code string for logging and programmatic handling.

## Error codes

Every code below is a specific, catchable case:

| Code      | iOS (`RainSDKError`)                                | Android (`RainError`)         | Meaning                                                                                                 |
| --------- | --------------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------- |
| RAIN\_101 | `.sdkNotInitialized`                                | `SdkNotInitialized`           | A method was called before the SDK was built, or no wallet provider is resolved.                        |
| RAIN\_102 | `.invalidConfig` / `.providerNotRegistered`         | `InvalidConfig`               | Invalid RPC URL, chain ID, or address, or no provider is registered for the requested id or capability. |
| RAIN\_103 | `.invalidRpcUrl`                                    | `InvalidRpcUrl`               | An RPC URL could not be parsed as a valid URL.                                                          |
| RAIN\_104 | `.rainApiNotConfigured`                             | `ApiNotConfigured`            | A Rain API method was called before `configureRainApi` / `rainApiCredentials`.                          |
| RAIN\_201 | `.tokenExpired`                                     | `TokenExpired`                | The wallet provider session token has expired or is no longer valid.                                    |
| RAIN\_202 | `.unauthorized`                                     | `Unauthorized`                | Invalid Rain API key or session, or insufficient permissions.                                           |
| RAIN\_301 | `.networkError`                                     | `NetworkError`                | Connectivity issue reaching an RPC endpoint or provider API.                                            |
| RAIN\_302 | `.apiError(statusCode:message:)`                    | `ApiError`                    | Non-auth HTTP failure from the Rain API (carries the status code).                                      |
| RAIN\_303 | `.signatureNotReady(status:retryAfter:)`            | `SignatureNotReady`           | Rain hasn't produced the withdrawal signature yet (carries `status` and optional `retryAfter`).         |
| RAIN\_304 | `.noCollateralContracts`                            | `NoCollateralContracts`       | The user has no collateral contracts.                                                                   |
| RAIN\_401 | `.userRejected`                                     | `UserRejected`                | The user cancelled the signing request in the wallet.                                                   |
| RAIN\_402 | `.insufficientFunds(required:available:)`           | `InsufficientFunds`           | Wallet balance too low for the amount or gas fees.                                                      |
| RAIN\_403 | `.transactionSimulationFailed`                      | `TransactionSimulationFailed` | Preflight simulation (`eth_call`) reverted before submission.                                           |
| RAIN\_404 | `.walletUnavailable`                                | `WalletUnavailable`           | The provider returned no usable wallet address (user hasn't created or connected a wallet).             |
| RAIN\_405 | `.withdrawalRevertedByNetwork`                      | `WithdrawalRevertedByNetwork` | The withdrawal reverted on-chain: often a duplicate withdrawal or an already-used signature.            |
| RAIN\_406 | `.invalidAmount(amount:reason:)`                    | `InvalidAmount`               | The amount is invalid for the token: more decimals than supported, or negative.                         |
| RAIN\_407 | `.walletNotAuthorized(walletAddress:proxyAddress:)` | `WalletNotAuthorized`         | The signing wallet is not in the collateral's admin set.                                                |
| RAIN\_501 | `.providerError`                                    | `ProviderError`               | An unhandled error from the wallet provider.                                                            |
| RAIN\_502 | `.internalLogicError`                               | `InternalError`               | EIP-712 encoding, ABI encoding, or internal state error.                                                |

## Handling errors

Switch on the specific cases you need to handle, with a default case for the rest:

<CodeGroup>
  ```swift iOS theme={null}
  do {
      let txHash = try await client.withdrawCollateral(/* ... */)
  } catch let error as RainSDKError {
      switch error {
      case .sdkNotInitialized:
          break // Rebuild the SDK and resolve a client
      case .tokenExpired:
          break // Re-authenticate the wallet provider
      case .userRejected:
          break // Show a retry prompt, expected user behavior
      case .walletNotAuthorized(let wallet, let proxy):
          print("wallet \(wallet) is not admin of \(proxy)")
      case .signatureNotReady(_, let retryAfter):
          scheduleRetry(after: retryAfter ?? 5)
      case .insufficientFunds(let required, let available):
          print("Need \(required), have \(available)")
      case .withdrawalRevertedByNetwork:
          break // Request a fresh withdrawal signature and retry
      case .networkError(let underlying):
          print("Network error: \(underlying.localizedDescription)")
      default:
          print("Error: \(error.errorCode)")
      }
  }
  ```

  ```kotlin Android theme={null}
  try {
      val result = client.withdrawCollateral(/* ... */)
  } catch (e: RainError) {
      when (e) {
          is RainError.SdkNotInitialized -> { /* Rebuild the SDK */ }
          is RainError.TokenExpired -> { /* Re-authenticate the wallet provider */ }
          is RainError.UserRejected -> { /* Show a retry prompt */ }
          is RainError.WalletNotAuthorized -> Log.w(TAG, "wallet ${e.walletAddress} is not admin of ${e.proxyAddress}")
          is RainError.SignatureNotReady -> retryAfter(e.retryAfter ?: 5)
          is RainError.InsufficientFunds -> { /* Show insufficient balance UI */ }
          is RainError.WithdrawalRevertedByNetwork -> { /* Request a fresh signature */ }
          is RainError.NetworkError -> { /* Retry or show connectivity error */ }
          else -> { println("Error: ${e.errorCode.code}") }
      }
  }
  ```
</CodeGroup>

## Accessing the error code

Every error exposes a string code (for example `"RAIN_101"`) for logging or analytics:

<CodeGroup>
  ```swift iOS theme={null}
  catch let error as RainSDKError {
      let code = error.errorCode              // "RAIN_101"
      let message = error.localizedDescription
  }
  ```

  ```kotlin Android theme={null}
  catch (e: RainError) {
      val code = e.errorCode.code // "RAIN_101"
      val message = e.message
  }
  ```
</CodeGroup>

## Troubleshooting

Common symptoms and their fixes:

| Symptom                               | Likely cause                                         | Fix                                                                                                                                                            |
| ------------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| RAIN\_101 on every call               | SDK not built or no provider resolved                | Call `build()` and resolve a client (`provider(...)`) before other methods                                                                                     |
| RAIN\_102 at startup                  | Malformed RPC URL / chain ID, or unknown provider id | Verify your `rpcEndpoints` and that the provider is registered                                                                                                 |
| RAIN\_104 on contract/signature fetch | Rain API credentials not set                         | Call `rainApiCredentials(...)` or `configureRainApi(...)` (see [the built-in Rain API client](/sdks/embedded-wallets-authentication#built-in-rain-api-client)) |
| RAIN\_201 after an idle period        | Provider session expired                             | Re-authenticate the wallet and rebuild the SDK                                                                                                                 |
| RAIN\_202 on a Rain API call          | Invalid API key or session                           | Check the key (the SDK already retried the session mint once)                                                                                                  |
| RAIN\_303 fetching a signature        | Signature still being produced                       | Retry after the `retryAfter` hint                                                                                                                              |
| RAIN\_401 during withdrawal           | User cancelled signing                               | Show a retry prompt (this is expected)                                                                                                                         |
| RAIN\_403 before send                 | Transaction would revert                             | Verify parameters, balances, and that the nonce hasn't been used                                                                                               |
| RAIN\_404 on balance query            | No wallet resolved                                   | Resolve the client with `provider(...)` before querying balances                                                                                               |
| RAIN\_405 on withdrawal               | Duplicate / used signature                           | Request a new withdrawal signature from the Rain API                                                                                                           |
| RAIN\_407 on withdrawal               | Wrong signing wallet                                 | Withdraw with a wallet listed in the contract's `adminAddresses`                                                                                               |

## What's next

<Columns cols={3}>
  <Card title="Embedded Wallets Overview" icon="rocket" href="/sdks/embedded-wallets-overview">
    Install the SDK and set up embedded wallets.
  </Card>

  <Card title="Balances & Transactions" icon="coins" href="/sdks/embedded-wallets-balances-and-transactions">
    Query balances and send tokens.
  </Card>

  <Card title="Withdraw Collateral" icon="arrow-right-from-bracket" href="/sdks/embedded-wallets-withdraw-collateral">
    Execute collateral withdrawals from your app.
  </Card>
</Columns>
