> For the complete documentation index, see [llms.txt](https://docs.snackprompt.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.snackprompt.com/bring-your-data-into-ai/how-to/how-to-handle-errors.md).

# How to Handle Errors

Learn how to handle API errors robustly.

### Problem

You want your application to handle errors gracefully without breaking the user experience.

### Solution

Implement error handling at 3 levels: validation, HTTP response, and retry.

### 1. Validation Before Sending

Avoid errors by validating data first:

```javascript
function validateRequest(query, tenantId) {
  if (!query || query.trim() === '') {
    throw new Error('Query is required');
  }

  if (!tenantId) {
    throw new Error('tenant_id is required');
  }

  return true;
}
```

### 2. HTTP Code Handling

```javascript
async function apiRequest(endpoint, body, apiKey) {
  const response = await fetch(endpoint, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': apiKey
    },
    body: JSON.stringify(body)
  });

  if (!response.ok) {
    const error = await response.json();

    switch (response.status) {
      case 400:
        throw new ValidationError(error.detail);

      case 404:
        throw new NotFoundError(error.detail);

      case 429:
        throw new RateLimitError(
          error.detail,
          response.headers.get('X-RateLimit-Reset')
        );

      case 500:
      case 503:
        throw new ServerError(error.detail);

      default:
        throw new ApiError(error.detail, response.status);
    }
  }

  return response.json();
}
```

### 3. Custom Error Classes

```javascript
class ApiError extends Error {
  constructor(message, status) {
    super(message);
    this.name = 'ApiError';
    this.status = status;
  }
}

class ValidationError extends ApiError {
  constructor(message) {
    super(message, 400);
    this.name = 'ValidationError';
  }
}

class NotFoundError extends ApiError {
  constructor(message) {
    super(message, 404);
    this.name = 'NotFoundError';
  }
}

class RateLimitError extends ApiError {
  constructor(message, resetTime) {
    super(message, 429);
    this.name = 'RateLimitError';
    this.resetTime = resetTime;
  }
}

class ServerError extends ApiError {
  constructor(message) {
    super(message, 500);
    this.name = 'ServerError';
  }
}
```

### 4. Retry with Exponential Backoff

For temporary errors (429, 500, 503):

```javascript
async function retryWithBackoff(fn, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      const shouldRetry =
        error instanceof RateLimitError ||
        error instanceof ServerError;

      if (!shouldRetry || attempt === maxRetries - 1) {
        throw error;
      }

      // Exponential backoff: 1s, 2s, 4s
      const waitTime = Math.pow(2, attempt) * 1000;
      const jitter = Math.random() * 1000;

      console.log(`Retry ${attempt + 1}/${maxRetries} in ${waitTime}ms`);
      await sleep(waitTime + jitter);
    }
  }
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
```

### 5. Complete Usage

```javascript
async function searchWithErrorHandling(query, tenantId, apiKey) {
  try {
    // Validation
    validateRequest(query, tenantId);

    // Request with retry
    const result = await retryWithBackoff(() =>
      apiRequest('https://api-integrations.snackprompt.com/v1/kb/search', {
        query,
        filters: { tenant_id: tenantId },
        limit: 5
      }, apiKey)
    );

    return result;

  } catch (error) {
    // Specific handling by type
    if (error instanceof ValidationError) {
      showUserMessage('Please fill in all fields');
    } else if (error instanceof NotFoundError) {
      showUserMessage('No results found');
    } else if (error instanceof RateLimitError) {
      showUserMessage('Too many requests. Please wait.');
    } else if (error instanceof ServerError) {
      showUserMessage('Service temporarily unavailable');
    } else {
      showUserMessage('Unexpected error. Please try again.');
      console.error('Unexpected error:', error);
    }

    // Re-throw for logging or upper-level handling
    throw error;
  }
}
```

### Python: Error Handling

```python
import requests
import time
import random

class ApiError(Exception):
    def __init__(self, message, status_code):
        super().__init__(message)
        self.status_code = status_code

def api_request(endpoint, body, api_key, max_retries=3):
    for attempt in range(max_retries):
        try:
            response = requests.post(
                endpoint,
                headers={'x-api-key': api_key},
                json=body
            )

            if response.ok:
                return response.json()

            error = response.json()

            if response.status_code == 400:
                raise ValueError(f"Validation error: {error['detail']}")

            if response.status_code == 404:
                raise FileNotFoundError(error['detail'])

            if response.status_code == 429:
                if attempt < max_retries - 1:
                    wait = (2 ** attempt) + random.uniform(0, 1)
                    time.sleep(wait)
                    continue
                raise ApiError("Rate limit exceeded", 429)

            if response.status_code >= 500:
                if attempt < max_retries - 1:
                    wait = (2 ** attempt) + random.uniform(0, 1)
                    time.sleep(wait)
                    continue
                raise ApiError("Server error", response.status_code)

            raise ApiError(error['detail'], response.status_code)

        except requests.exceptions.ConnectionError:
            if attempt < max_retries - 1:
                time.sleep(2 ** attempt)
                continue
            raise

    raise ApiError("Max retries exceeded", 0)
```

### Common Errors and Solutions

| Error                   | Cause              | Solution                     |
| ----------------------- | ------------------ | ---------------------------- |
| `tenant_id is required` | Missing tenant\_id | Add tenant\_id to filters    |
| `query is required`     | Empty query        | Validate before sending      |
| `Rate limit exceeded`   | Too many requests  | Implement retry with backoff |
| `Internal server error` | Server error       | Automatic retry              |
| `Service unavailable`   | Maintenance        | Wait and try again           |

### Error Logging

```javascript
function logError(error, context) {
  console.error({
    timestamp: new Date().toISOString(),
    error: {
      name: error.name,
      message: error.message,
      status: error.status
    },
    context
  });

  // Send to monitoring service
  // sendToMonitoring(error, context);
}
```

### Related

* [Error Codes](/bring-your-data-into-ai/reference/error-codes.md)
* [Endpoints](/bring-your-data-into-ai/reference/endpoints.md)
