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

# Error Handling

> Understanding and handling API errors effectively

## Overview

The Mighty Networks API uses standard HTTP status codes to indicate the success or failure of requests. Proper error handling is essential for building robust integrations.

## HTTP Status Codes

| Status Code | Meaning               | Description                              |
| ----------- | --------------------- | ---------------------------------------- |
| **200**     | OK                    | Request succeeded                        |
| **201**     | Created               | Resource created successfully            |
| **204**     | No Content            | Request succeeded with no response body  |
| **400**     | Bad Request           | Invalid parameters or malformed request  |
| **401**     | Unauthorized          | Missing or invalid API token             |
| **403**     | Forbidden             | Valid token but insufficient permissions |
| **404**     | Not Found             | Resource doesn't exist                   |
| **409**     | Conflict              | Resource conflict (e.g., duplicate)      |
| **422**     | Unprocessable Entity  | Validation error                         |
| **429**     | Too Many Requests     | Rate limit exceeded                      |
| **500**     | Internal Server Error | Server-side error                        |
| **502**     | Bad Gateway           | Temporary server issue                   |
| **503**     | Service Unavailable   | Service temporarily down                 |

## Error Response Format

All errors return a JSON response containing an `error` property:

```json theme={null}
{
  "error": "Human-readable error description"
}
```

### Example Error Responses

#### Authentication Error (401)

```json theme={null}
{
  "error": "Invalid API token provided"
}
```

#### Not Found Error (404)

```json theme={null}
{
  "error": "Member not found"
}
```

#### Validation Error (422)

```json theme={null}
{
  "error": "Invalid role value. Must be one of: admin, host, member"
}
```

## Common Error Types

### 1. Authentication Errors (401)

**Causes:**

* Missing `Authorization` header
* Invalid or expired API token
* Malformed Bearer token

**Example:**

```javascript theme={null}
const response = await fetch(
  `https://api.mn.co/admin/v1/networks/${NETWORK_ID}/members`,
  {
    headers: { 'Authorization': 'Bearer INVALID_TOKEN' }
  }
);

// Response: 401 Unauthorized
```

**How to Handle:**

```javascript theme={null}
if (response.status === 401) {
  console.error('Authentication failed. Check your API token.');
  // Refresh token or prompt user to re-authenticate
}
```

### 2. Permission Errors (403)

**Causes:**

* Token lacks required scopes/permissions
* Attempting to access resources outside your network
* Admin-only operation with non-admin token

**Example:**

```javascript theme={null}
// Token doesn't have 'members:write' permission
const response = await fetch(
  `https://api.mn.co/admin/v1/networks/${NETWORK_ID}/members/${MEMBER_ID}/`,
  {
    method: 'PATCH',
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ role: 'admin' })
  }
);

// Response: 403 Forbidden
```

**How to Handle:**

```javascript theme={null}
if (response.status === 403) {
  console.error('Insufficient permissions for this operation');
  // Check token scopes or request elevated permissions
}
```

### 3. Resource Not Found (404)

**Causes:**

* Invalid resource ID
* Resource was deleted
* Incorrect network ID
* Typo in endpoint URL

**How to Handle:**

```javascript theme={null}
const response = await fetch(
  `https://api.mn.co/admin/v1/networks/${NETWORK_ID}/members/${MEMBER_ID}/`,
  { headers: { 'Authorization': `Bearer ${API_TOKEN}` } }
);

if (response.status === 404) {
  console.error('Member not found. They may have been deleted.');
  // Handle missing resource gracefully
  return null;
}
```

### 4. Validation Errors (422)

**Causes:**

* Invalid field values
* Missing required fields
* Data format errors

**How to Handle:**

```javascript theme={null}
const response = await fetch(
  `https://api.mn.co/admin/v1/networks/${NETWORK_ID}/members/${MEMBER_ID}/`,
  {
    method: 'PATCH',
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ role: 'invalid_role' })
  }
);

if (response.status === 422) {
  const error = await response.json();
  console.error('Validation error:', error.message);
  // Display validation errors to user
}
```

### 5. Rate Limit Errors (429)

**Causes:**

* Exceeded requests per minute limit
* Too many requests in short time

**How to Handle:**

See the [Rate Limits](/admin-api#rate-limit-and-quota) guide for detailed handling strategies.

```javascript theme={null}
if (response.status === 429) {
  const retryAfter = response.headers.get('Retry-After') || 60;
  console.log(`Rate limited. Retrying in ${retryAfter}s`);
  await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
  // Retry request
}
```

## Error Handling Patterns

### Basic Error Handling

```javascript theme={null}
async function fetchMember(networkId, memberId, apiToken) {
  try {
    const response = await fetch(
      `https://api.mn.co/admin/v1/networks/${networkId}/members/${memberId}/`,
      { headers: { 'Authorization': `Bearer ${apiToken}` } }
    );

    if (!response.ok) {
      const error = await response.json();
      throw new Error(`API Error: ${error.message}`);
    }

    return await response.json();
  } catch (error) {
    console.error('Failed to fetch member:', error);
    throw error;
  }
}
```

### Comprehensive Error Handler

```javascript theme={null}
async function makeAPIRequest(url, options = {}) {
  try {
    const response = await fetch(url, options);

    // Handle different status codes
    switch (response.status) {
      case 200:
      case 201:
      case 204:
        return await response.json().catch(() => ({}));

      case 400:
        const badRequest = await response.json();
        throw new Error(`Bad Request: ${badRequest.message}`);

      case 401:
        throw new Error('Authentication failed. Please check your API token.');

      case 403:
        throw new Error('Permission denied. Check your token permissions.');

      case 404:
        throw new Error('Resource not found.');

      case 409:
        const conflict = await response.json();
        throw new Error(`Conflict: ${conflict.message}`);

      case 422:
        const validation = await response.json();
        throw new Error(`Validation Error: ${validation.message}`);

      case 429:
        const retryAfter = response.headers.get('Retry-After') || 60;
        throw new Error(`Rate limited. Retry after ${retryAfter}s`);

      case 500:
      case 502:
      case 503:
        throw new Error('Server error. Please try again later.');

      default:
        throw new Error(`Unexpected error: ${response.status}`);
    }
  } catch (error) {
    console.error('API request failed:', error);
    throw error;
  }
}
```

### Custom Error Classes

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

class AuthenticationError extends APIError {
  constructor(message) {
    super(401, message, 'authentication_error');
    this.name = 'AuthenticationError';
  }
}

class NotFoundError extends APIError {
  constructor(resource) {
    super(404, `${resource} not found`, 'not_found');
    this.name = 'NotFoundError';
  }
}

class RateLimitError extends APIError {
  constructor(retryAfter) {
    super(429, 'Rate limit exceeded', 'rate_limit_exceeded');
    this.name = 'RateLimitError';
    this.retryAfter = retryAfter;
  }
}

async function fetchWithCustomErrors(url, options) {
  const response = await fetch(url, options);

  if (response.ok) {
    return await response.json();
  }

  const error = await response.json();

  switch (response.status) {
    case 401:
      throw new AuthenticationError(error.message);
    case 404:
      throw new NotFoundError('Resource');
    case 429:
      const retryAfter = response.headers.get('Retry-After');
      throw new RateLimitError(retryAfter);
    default:
      throw new APIError(response.status, error.message, error.error);
  }
}

// Usage
try {
  const member = await fetchWithCustomErrors(url, options);
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.log('Please re-authenticate');
  } else if (error instanceof NotFoundError) {
    console.log('Resource was deleted');
  } else if (error instanceof RateLimitError) {
    console.log(`Wait ${error.retryAfter}s before retrying`);
  }
}
```

### Retry Logic

```javascript theme={null}
async function fetchWithRetry(url, options, maxRetries = 3) {
  let lastError;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);

      // Don't retry on client errors (except 429)
      if (response.status >= 400 && response.status < 500 && response.status !== 429) {
        const error = await response.json();
        throw new Error(error.message);
      }

      // Retry on server errors and rate limits
      if (!response.ok) {
        const waitTime = response.status === 429
          ? parseInt(response.headers.get('Retry-After') || '60') * 1000
          : Math.pow(2, attempt) * 1000;

        console.log(`Request failed (attempt ${attempt + 1}/${maxRetries}). Retrying in ${waitTime}ms`);
        await new Promise(resolve => setTimeout(resolve, waitTime));
        continue;
      }

      return await response.json();
    } catch (error) {
      lastError = error;
      if (attempt === maxRetries - 1) {
        throw error;
      }
    }
  }

  throw lastError;
}
```

## Python Error Handling

### Basic Pattern

```python theme={null}
import requests

def fetch_member(network_id, member_id, api_token):
    try:
        response = requests.get(
            f"https://api.mn.co/admin/v1/networks/{network_id}/members/{member_id}/",
            headers={"Authorization": f"Bearer {api_token}"}
        )

        response.raise_for_status()
        return response.json()

    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 401:
            print("Authentication failed")
        elif e.response.status_code == 404:
            print("Member not found")
        elif e.response.status_code == 429:
            print("Rate limit exceeded")
        else:
            print(f"HTTP error: {e}")
        raise

    except requests.exceptions.RequestException as e:
        print(f"Request failed: {e}")
        raise
```

### Custom Exception Classes

```python theme={null}
class APIException(Exception):
    def __init__(self, status_code, message, error_code):
        self.status_code = status_code
        self.message = message
        self.error_code = error_code
        super().__init__(self.message)

class AuthenticationException(APIException):
    def __init__(self, message):
        super().__init__(401, message, 'authentication_error')

class NotFoundException(APIException):
    def __init__(self, resource):
        super().__init__(404, f"{resource} not found", 'not_found')

def make_request(url, headers):
    response = requests.get(url, headers=headers)

    if response.ok:
        return response.json()

    error_data = response.json()

    if response.status_code == 401:
        raise AuthenticationException(error_data.get('message'))
    elif response.status_code == 404:
        raise NotFoundException('Resource')
    else:
        raise APIException(
            response.status_code,
            error_data.get('message'),
            error_data.get('error')
        )
```

### Retry with Backoff

```python theme={null}
import time
from functools import wraps

def retry_with_backoff(max_retries=3):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_retries):
                try:
                    response = func(*args, **kwargs)

                    if response.status_code >= 500 or response.status_code == 429:
                        if attempt < max_retries - 1:
                            wait_time = 2 ** attempt
                            print(f"Retry {attempt + 1}/{max_retries} in {wait_time}s")
                            time.sleep(wait_time)
                            continue

                    return response

                except Exception as e:
                    if attempt == max_retries - 1:
                        raise
                    time.sleep(2 ** attempt)

            return response

        return wrapper
    return decorator

@retry_with_backoff(max_retries=5)
def make_api_call(url, headers):
    return requests.get(url, headers=headers)
```

## Best Practices

1. **Always Check Status Codes** - Don't assume requests succeed
2. **Parse Error Messages** - Extract meaningful information from error responses
3. **Implement Retries** - Retry transient errors (5xx, 429)
4. **Don't Retry 4xx Errors** - Client errors won't succeed on retry (except 429)
5. **Log Errors** - Keep detailed error logs for debugging
6. **Handle Gracefully** - Provide user-friendly error messages
7. **Monitor Errors** - Track error rates to identify issues
8. **Validate Input** - Catch errors before making API calls

## Common Pitfalls

### ❌ Ignoring Status Codes

```javascript theme={null}
// Bad: Assumes success
const data = await response.json();
```

### ✅ Check Response Status

```javascript theme={null}
// Good: Verify success
if (!response.ok) {
  throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
```

### ❌ Generic Error Messages

```javascript theme={null}
// Bad: Unhelpful error
catch (error) {
  console.log('Error occurred');
}
```

### ✅ Specific Error Handling

```javascript theme={null}
// Good: Actionable errors
catch (error) {
  if (error.status === 404) {
    console.log('Member was deleted');
  } else {
    console.log(`Failed: ${error.message}`);
  }
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Rate Limits" icon="gauge" href="/admin-api#rate-limit-and-quota">
    Handle rate limit errors effectively.
  </Card>

  <Card title="Authentication" icon="key" href="/authentication">
    Troubleshoot authentication errors.
  </Card>

  <Card title="Admin API" icon="code" href="/admin-api">
    Review API endpoint documentation.
  </Card>

  <Card title="Pagination" icon="list" href="/api-pagination">
    Handle pagination errors.
  </Card>
</CardGroup>
