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

# Error Handling

> Handle SDK errors gracefully with error codes and retry strategies

## RebelfiError Class

All API errors are thrown as `RebelfiError` instances with structured information:

```typescript theme={null}
import { RebelfiError, ErrorCode } from '@rebelfi/sdk';

try {
  await client.operations.supply(request);
} catch (error) {
  if (error instanceof RebelfiError) {
    console.error('Message:', error.message);
    console.error('Status:', error.statusCode);
    console.error('Code:', error.code);
    console.error('Details:', error.details);
  }
}
```

### Properties

| Property     | Type        | Description                  |
| ------------ | ----------- | ---------------------------- |
| `message`    | `string`    | Human-readable error message |
| `statusCode` | `number`    | HTTP status code             |
| `code`       | `ErrorCode` | Machine-readable error code  |
| `details`    | `object`    | Additional error context     |

### Checking Error Type

Use the `.is()` method to check for specific errors:

```typescript theme={null}
if (error instanceof RebelfiError) {
  if (error.is(ErrorCode.INSUFFICIENT_BALANCE)) {
    // Show "insufficient funds" message to user
  } else if (error.is(ErrorCode.OPERATION_EXPIRED)) {
    // Retry with a new operation
  }
}
```

## Error Codes

### Validation Errors (4xx)

| Code              | Description                            | Recommended Action             |
| ----------------- | -------------------------------------- | ------------------------------ |
| `INVALID_AMOUNT`  | Amount is zero, negative, or malformed | Validate amount before calling |
| `INVALID_ADDRESS` | Wallet address format is invalid       | Validate address format        |
| `INVALID_TOKEN`   | Token address not recognized           | Use address from strategy      |

```typescript theme={null}
if (error.is(ErrorCode.INVALID_AMOUNT)) {
  // Show validation error to user
  showError('Please enter a valid amount');
}
```

### Business Logic Errors (4xx)

| Code                          | Description                                    | Recommended Action                    |
| ----------------------------- | ---------------------------------------------- | ------------------------------------- |
| `INSUFFICIENT_BALANCE`        | Wallet balance too low for operation           | Show balance, suggest lower amount    |
| `STRATEGY_NOT_ACTIVE`         | Strategy is paused or deprecated               | Refresh venues, pick another          |
| `ALLOCATION_NOT_FOUND`        | No position exists at this venue               | Check allocations first               |
| `OPERATION_EXPIRED`           | Unsigned transaction expired                   | Create new operation                  |
| `OPERATION_ALREADY_SUBMITTED` | Transaction already submitted                  | Check operation status                |
| `TOKEN_MISMATCH`              | Token doesn't match strategy                   | Use tokenAddress from strategy        |
| `INVALID_OPERATION_STATUS`    | Operation in wrong state for action            | Check operation status first          |
| `OPERATION_IN_PROGRESS`       | Another operation is executing for this wallet | Wait for it to complete, or cancel it |

```typescript theme={null}
switch (error.code) {
  case ErrorCode.INSUFFICIENT_BALANCE:
    const balance = error.details?.available;
    showError(`Insufficient balance. Available: ${balance}`);
    break;

  case ErrorCode.STRATEGY_NOT_ACTIVE:
    // Refresh and show available strategies
    await refreshStrategies();
    showError('This strategy is no longer available');
    break;

  case ErrorCode.OPERATION_EXPIRED:
    // Automatically retry
    return await retryOperation();

  case ErrorCode.OPERATION_IN_PROGRESS:
    // Another operation is executing — wait or cancel it
    const { operationId, walletId } = error.details as { operationId: number; walletId: number };
    showError(`Operation ${operationId} is still executing. Please wait for it to complete.`);
    break;
}
```

### Resource Errors (404)

| Code                     | Description                  | Recommended Action                                 |
| ------------------------ | ---------------------------- | -------------------------------------------------- |
| `VENUE_NOT_FOUND`        | Venue ID doesn't exist       | Refresh venue list                                 |
| `STRATEGY_NOT_FOUND`     | Strategy ID doesn't exist    | Refresh venue list                                 |
| `OPERATION_NOT_FOUND`    | Operation ID doesn't exist   | May have been cleaned up                           |
| `TRANSACTION_NOT_FOUND`  | Transaction ID doesn't exist | Check operation status                             |
| `WALLET_NOT_FOUND`       | Wallet not registered        | Register the wallet first via `wallets.register()` |
| `ORGANIZATION_NOT_FOUND` | API key's org not found      | Check API key                                      |
| `TOKEN_NOT_FOUND`        | Token not supported          | Check supported tokens                             |

### Simulation Errors (4xx)

| Code                | Description                        | Recommended Action     |
| ------------------- | ---------------------------------- | ---------------------- |
| `INSUFFICIENT_GAS`  | Not enough SOL for transaction fee | Prompt user to add SOL |
| `SIMULATION_FAILED` | Transaction simulation failed      | Check error details    |

```typescript theme={null}
if (error.is(ErrorCode.INSUFFICIENT_GAS)) {
  showError('Please add SOL to cover transaction fees');
}
```

### Authentication Errors (401/403)

| Code               | Description              | Recommended Action          |
| ------------------ | ------------------------ | --------------------------- |
| `INVALID_API_KEY`  | API key not recognized   | Check API key configuration |
| `API_KEY_DISABLED` | API key has been revoked | Contact support             |

### Rate Limiting (429)

| Code                  | Description       | Recommended Action |
| --------------------- | ----------------- | ------------------ |
| `RATE_LIMIT_EXCEEDED` | Too many requests | Implement backoff  |

```typescript theme={null}
if (error.is(ErrorCode.RATE_LIMIT_EXCEEDED)) {
  // Exponential backoff
  await delay(retryCount * 1000);
  return await retry();
}
```

### Network Errors

| Code            | Description                | Recommended Action      |
| --------------- | -------------------------- | ----------------------- |
| `TIMEOUT`       | Request timed out          | Retry with backoff      |
| `NETWORK_ERROR` | Network connectivity issue | Check connection, retry |
| `UNKNOWN_ERROR` | Unexpected error           | Log and report          |

## Error Details by Code

Many errors include a `details` object with structured context. Here are the fields returned for each error code:

| Error Code              | Details Fields                                | Example                                                      |
| ----------------------- | --------------------------------------------- | ------------------------------------------------------------ |
| `INSUFFICIENT_BALANCE`  | `requested: string`, `available: string`      | `{ "requested": "1000000", "available": "500000" }`          |
| `OPERATION_IN_PROGRESS` | `operationId: number`, `walletId: number`     | `{ "operationId": 123, "walletId": 45 }`                     |
| `INSUFFICIENT_GAS`      | `walletAddress: string`, `blockchain: string` | `{ "walletAddress": "So11...abc", "blockchain": "solana" }`  |
| `SIMULATION_FAILED`     | `logs?: string[]`, `unitsConsumed?: number`   | `{ "logs": ["Program log: Error"], "unitsConsumed": 50000 }` |
| `TOKEN_MISMATCH`        | `expected: string`, `actual: string`          | `{ "expected": "USDC", "actual": "USDT" }`                   |
| `STRATEGY_NOT_ACTIVE`   | `strategyId: number`                          | `{ "strategyId": 1 }`                                        |
| `ALLOCATION_NOT_FOUND`  | `strategyId: number`, `walletAddress: string` | `{ "strategyId": 1, "walletAddress": "So11...abc" }`         |

***

## Transaction Failure Codes

When a transaction fails, check the `failureCode` for details:

```typescript theme={null}
const operation = await client.operations.get(operationId);

if (operation.status === 'FAILED') {
  const tx = operation.transactions[0];

  switch (tx.failureCode) {
    case 'REVERTED':
      console.error('Contract reverted:', tx.revertReason);
      break;
    case 'OUT_OF_GAS':
      console.error('Transaction ran out of gas');
      break;
    case 'TIMEOUT':
      console.error('Transaction not confirmed in time');
      break;
    case 'INSUFFICIENT_FUNDS':
      console.error('Not enough SOL for gas');
      break;
  }
}
```

| Code                 | Description                          |
| -------------------- | ------------------------------------ |
| `REVERTED`           | Smart contract execution reverted    |
| `OUT_OF_GAS`         | Transaction ran out of gas           |
| `TIMEOUT`            | Not included in block within timeout |
| `NONCE_TOO_LOW`      | Nonce already used (duplicate tx)    |
| `INSUFFICIENT_FUNDS` | Not enough native token for gas      |
| `REPLACED`           | Transaction replaced by another      |
| `DROPPED`            | Dropped from mempool                 |
| `UNKNOWN`            | Unknown failure reason               |

## Error Handling Patterns

### Comprehensive Handler

```typescript theme={null}
import { RebelfiError, ErrorCode } from '@rebelfi/sdk';

async function handleSupply(params: SupplyParams) {
  try {
    const operation = await client.operations.supply(params);
    return { success: true, operation };
  } catch (error) {
    if (!(error instanceof RebelfiError)) {
      // Unexpected error
      console.error('Unexpected error:', error);
      return { success: false, error: 'An unexpected error occurred' };
    }

    // User-fixable errors
    if (error.is(ErrorCode.INSUFFICIENT_BALANCE)) {
      return {
        success: false,
        error: 'Insufficient balance',
        userAction: 'deposit_more'
      };
    }

    if (error.is(ErrorCode.INSUFFICIENT_GAS)) {
      return {
        success: false,
        error: 'Need SOL for transaction fee',
        userAction: 'add_sol'
      };
    }

    // Retryable errors
    if (error.is(ErrorCode.RATE_LIMIT_EXCEEDED) ||
        error.is(ErrorCode.TIMEOUT) ||
        error.is(ErrorCode.NETWORK_ERROR)) {
      return {
        success: false,
        error: 'Temporary error',
        retryable: true
      };
    }

    // Strategy issues
    if (error.is(ErrorCode.STRATEGY_NOT_ACTIVE) ||
        error.is(ErrorCode.STRATEGY_NOT_FOUND)) {
      return {
        success: false,
        error: 'Strategy unavailable',
        userAction: 'select_different_strategy'
      };
    }

    // Auth issues
    if (error.is(ErrorCode.INVALID_API_KEY) ||
        error.is(ErrorCode.API_KEY_DISABLED)) {
      console.error('API key issue:', error.message);
      return {
        success: false,
        error: 'Service configuration error'
      };
    }

    // Unknown API error
    return {
      success: false,
      error: error.message
    };
  }
}
```

### Retry with Backoff

```typescript theme={null}
async function withRetry<T>(
  fn: () => Promise<T>,
  options: { maxAttempts?: number; baseDelay?: number } = {}
): Promise<T> {
  const { maxAttempts = 3, baseDelay = 1000 } = options;

  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (!(error instanceof RebelfiError)) throw error;

      const isRetryable =
        error.is(ErrorCode.TIMEOUT) ||
        error.is(ErrorCode.NETWORK_ERROR) ||
        error.is(ErrorCode.RATE_LIMIT_EXCEEDED);

      if (!isRetryable || attempt === maxAttempts) {
        throw error;
      }

      const delay = baseDelay * Math.pow(2, attempt - 1);
      await new Promise(r => setTimeout(r, delay));
    }
  }

  throw new Error('Max attempts reached');
}

// Usage
const operation = await withRetry(() =>
  client.operations.supply(params)
);
```

### Operation Expiry Handling

```typescript theme={null}
async function supplyWithRetry(params: SupplyParams) {
  const maxRetries = 2;

  for (let i = 0; i < maxRetries; i++) {
    try {
      const operation = await client.operations.supply(params);

      // Check if we have enough time
      const expiresAt = new Date(operation.expiresAt);
      const timeLeft = expiresAt.getTime() - Date.now();

      if (timeLeft < 30000) {
        console.warn('Operation expires soon, may need retry');
      }

      return operation;
    } catch (error) {
      if (error instanceof RebelfiError &&
          error.is(ErrorCode.OPERATION_EXPIRED)) {
        console.log('Operation expired, retrying...');
        continue;
      }
      throw error;
    }
  }

  throw new Error('Failed after retries');
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="TypeScript Reference" icon="brackets-curly" href="/sdk/reference/client">
    Complete type definitions
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/introduction">
    REST API documentation
  </Card>
</CardGroup>
