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

# Error Handling

> Comprehensive guide to handling errors and implementing resilient JobHive integrations

## Error Response Format

All JobHive API errors follow a consistent JSON structure to help you handle them programmatically:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error description",
    "details": {
      "field": "specific_field_name",
      "value": "invalid_value",
      "additional_context": "extra_information"
    }
  },
  "meta": {
    "timestamp": "2024-01-15T10:30:00Z",
    "request_id": "req_abc123def456",
    "documentation_url": "https://docs.jobhive.ai/api-reference/errors#ERROR_CODE"
  }
}
```

## HTTP Status Codes

JobHive uses standard HTTP status codes to indicate the success or failure of API requests:

<AccordionGroup>
  <Accordion title="2xx Success Codes">
    | Code    | Description | When Used                                     |
    | ------- | ----------- | --------------------------------------------- |
    | **200** | OK          | Successful GET, PATCH, DELETE requests        |
    | **201** | Created     | Successful POST requests (interview creation) |
    | **202** | Accepted    | Asynchronous operations initiated             |
    | **204** | No Content  | Successful DELETE with no response body       |
  </Accordion>

  <Accordion title="4xx Client Error Codes">
    | Code    | Description          | Common Causes                                   |
    | ------- | -------------------- | ----------------------------------------------- |
    | **400** | Bad Request          | Invalid request parameters, malformed JSON      |
    | **401** | Unauthorized         | Missing or invalid API key                      |
    | **403** | Forbidden            | Insufficient permissions for operation          |
    | **404** | Not Found            | Interview ID doesn't exist                      |
    | **409** | Conflict             | Resource already exists or state conflict       |
    | **422** | Unprocessable Entity | Valid JSON but business logic validation failed |
    | **429** | Too Many Requests    | Rate limit exceeded                             |
  </Accordion>

  <Accordion title="5xx Server Error Codes">
    | Code    | Description           | Handling Strategy                          |
    | ------- | --------------------- | ------------------------------------------ |
    | **500** | Internal Server Error | Retry with exponential backoff             |
    | **502** | Bad Gateway           | Temporary infrastructure issue, retry      |
    | **503** | Service Unavailable   | Planned maintenance, check status page     |
    | **504** | Gateway Timeout       | Request timeout, retry with longer timeout |
  </Accordion>
</AccordionGroup>

## Common Error Types

### Authentication Errors (401)

<Tabs>
  <Tab title="Missing API Key">
    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "AUTHENTICATION_REQUIRED",
        "message": "API key is required. Include 'Authorization: Bearer YOUR_API_KEY' header.",
        "details": {
          "header_missing": "Authorization"
        }
      }
    }
    ```

    **Solution**: Add the Authorization header to your request

    ```javascript theme={null}
    headers: {
      'Authorization': `Bearer ${process.env.JOBHIVE_API_KEY}`,
      'Content-Type': 'application/json'
    }
    ```
  </Tab>

  <Tab title="Invalid API Key">
    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "INVALID_API_KEY",
        "message": "The provided API key is invalid or has been revoked.",
        "details": {
          "key_prefix": "jh_live_abc123"
        }
      }
    }
    ```

    **Solution**: Verify your API key in the dashboard and regenerate if necessary
  </Tab>

  <Tab title="Expired API Key">
    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "API_KEY_EXPIRED",
        "message": "Your API key has expired. Please generate a new one.",
        "details": {
          "expired_at": "2024-01-15T10:30:00Z",
          "renewal_url": "https://app.jobhive.ai/settings/api-keys"
        }
      }
    }
    ```

    **Solution**: Generate a new API key and update your application
  </Tab>
</Tabs>

### Validation Errors (400/422)

<Tabs>
  <Tab title="Missing Required Fields">
    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "VALIDATION_ERROR",
        "message": "Required fields are missing",
        "details": {
          "missing_fields": ["candidate_email", "position"],
          "provided_fields": ["skills", "duration_minutes"]
        }
      }
    }
    ```

    **Solution**: Include all required fields in your request
  </Tab>

  <Tab title="Invalid Field Values">
    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "VALIDATION_ERROR", 
        "message": "Invalid field values provided",
        "details": {
          "candidate_email": "Invalid email format",
          "duration_minutes": "Must be between 15 and 90 minutes",
          "difficulty": "Must be one of: junior, intermediate, senior, expert"
        }
      }
    }
    ```

    **Solution**: Validate input data before sending requests

    ```javascript theme={null}
    function validateInterviewData(data) {
      const errors = {};
      
      if (!data.candidate_email?.includes('@')) {
        errors.candidate_email = 'Valid email required';
      }
      
      if (!data.position || data.position.length < 2) {
        errors.position = 'Position must be at least 2 characters';
      }
      
      if (data.duration_minutes < 15 || data.duration_minutes > 90) {
        errors.duration_minutes = 'Duration must be between 15-90 minutes';
      }
      
      if (Object.keys(errors).length > 0) {
        throw new ValidationError(errors);
      }
    }
    ```
  </Tab>
</Tabs>

### Rate Limiting (429)

```json theme={null}
{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests. Please slow down.",
    "details": {
      "limit": 300,
      "current": 301,
      "reset_at": "2024-01-15T10:31:00Z",
      "retry_after": 60
    }
  }
}
```

**Headers included in rate limit responses:**

```http theme={null}
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640995260
X-RateLimit-Retry-After: 60
```

### Resource Errors (404/409)

<Tabs>
  <Tab title="Interview Not Found">
    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "INTERVIEW_NOT_FOUND",
        "message": "Interview with ID 'int_invalid123' not found",
        "details": {
          "interview_id": "int_invalid123",
          "suggestion": "Verify the interview ID is correct"
        }
      }
    }
    ```
  </Tab>

  <Tab title="Invalid State Transition">
    ```json theme={null}
    {
      "success": false,
      "error": {
        "code": "INVALID_STATE_TRANSITION",
        "message": "Cannot update a completed interview",
        "details": {
          "current_status": "completed",
          "attempted_action": "update",
          "allowed_actions": ["get", "get_results"]
        }
      }
    }
    ```
  </Tab>
</Tabs>

## Error Handling Patterns

### Basic Error Handling

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function handleJobHiveRequest(url, options) {
    try {
      const response = await fetch(url, {
        ...options,
        headers: {
          'Authorization': `Bearer ${process.env.JOBHIVE_API_KEY}`,
          'Content-Type': 'application/json',
          ...options.headers
        }
      });

      // Parse response
      const data = await response.json();

      if (!response.ok) {
        throw new JobHiveAPIError(response.status, data.error);
      }

      return data;
    } catch (error) {
      if (error instanceof JobHiveAPIError) {
        throw error; // Re-throw API errors
      }
      
      // Network or parsing errors
      throw new Error(`Request failed: ${error.message}`);
    }
  }

  class JobHiveAPIError extends Error {
    constructor(status, errorData) {
      super(errorData.message);
      this.name = 'JobHiveAPIError';
      this.status = status;
      this.code = errorData.code;
      this.details = errorData.details;
    }
  }

  // Usage
  try {
    const interview = await handleJobHiveRequest('/interviews', {
      method: 'POST',
      body: JSON.stringify(interviewData)
    });
    console.log('Interview created:', interview.data.id);
  } catch (error) {
    if (error instanceof JobHiveAPIError) {
      console.error(`API Error (${error.code}):`, error.message);
      console.error('Details:', error.details);
    } else {
      console.error('Request Error:', error.message);
    }
  }
  ```

  ```python Python theme={null}
  import requests
  from typing import Dict, Any
  import time

  class JobHiveAPIError(Exception):
      def __init__(self, status_code: int, error_data: Dict[str, Any]):
          self.status_code = status_code
          self.code = error_data.get('code')
          self.message = error_data.get('message')
          self.details = error_data.get('details', {})
          super().__init__(self.message)

  class JobHiveClient:
      def __init__(self, api_key: str):
          self.api_key = api_key
          self.base_url = 'https://backend.jobhive.ai/v1'
          
      def make_request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
          url = f"{self.base_url}/{endpoint.lstrip('/')}"
          
          headers = kwargs.get('headers', {})
          headers.update({
              'Authorization': f'Bearer {self.api_key}',
              'Content-Type': 'application/json'
          })
          kwargs['headers'] = headers
          
          try:
              response = requests.request(method, url, **kwargs)
              
              # Try to parse JSON response
              try:
                  data = response.json()
              except ValueError:
                  # Non-JSON response
                  data = {'message': response.text}
              
              if not response.ok:
                  raise JobHiveAPIError(response.status_code, data.get('error', data))
              
              return data
              
          except requests.RequestException as e:
              raise Exception(f"Request failed: {e}")

  # Usage
  client = JobHiveClient(os.environ['JOBHIVE_API_KEY'])

  try:
      interview = client.make_request('POST', '/interviews', json={
          'candidate_email': 'test@example.com',
          'position': 'Software Engineer',
          'skills': ['Python', 'Django']
      })
      print(f"Interview created: {interview['data']['id']}")
      
  except JobHiveAPIError as e:
      print(f"API Error ({e.code}): {e.message}")
      if e.details:
          print(f"Details: {e.details}")
          
  except Exception as e:
      print(f"Request Error: {e}")
  ```
</CodeGroup>

### Retry Logic with Exponential Backoff

<CodeGroup>
  ```javascript Retry Implementation theme={null}
  class RetryableJobHiveClient {
    constructor(apiKey, maxRetries = 3) {
      this.apiKey = apiKey;
      this.maxRetries = maxRetries;
      this.baseUrl = 'https://backend.jobhive.ai/v1';
    }

    async makeRequestWithRetry(endpoint, options = {}, attempt = 1) {
      try {
        const response = await fetch(`${this.baseUrl}${endpoint}`, {
          ...options,
          headers: {
            'Authorization': `Bearer ${this.apiKey}`,
            'Content-Type': 'application/json',
            ...options.headers
          }
        });

        const data = await response.json();

        if (!response.ok) {
          // Check if error is retryable
          if (this.isRetryableError(response.status) && attempt <= this.maxRetries) {
            const delay = this.calculateDelay(attempt, response.headers);
            console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
            
            await this.sleep(delay);
            return this.makeRequestWithRetry(endpoint, options, attempt + 1);
          }
          
          throw new JobHiveAPIError(response.status, data.error);
        }

        return data;
      } catch (error) {
        if (error instanceof JobHiveAPIError) {
          throw error;
        }
        
        // Network errors - retry if we have attempts left
        if (attempt <= this.maxRetries) {
          const delay = this.calculateDelay(attempt);
          console.log(`Network error, retrying in ${delay}ms...`);
          
          await this.sleep(delay);
          return this.makeRequestWithRetry(endpoint, options, attempt + 1);
        }
        
        throw error;
      }
    }

    isRetryableError(status) {
      // Retry on server errors and rate limiting
      return status >= 500 || status === 429;
    }

    calculateDelay(attempt, headers = {}) {
      // Use Retry-After header if available
      const retryAfter = headers.get?.('X-RateLimit-Retry-After');
      if (retryAfter) {
        return parseInt(retryAfter) * 1000;
      }
      
      // Exponential backoff: 1s, 2s, 4s, 8s...
      return Math.min(1000 * Math.pow(2, attempt - 1), 30000);
    }

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

  // Usage
  const client = new RetryableJobHiveClient(process.env.JOBHIVE_API_KEY);

  try {
    const interview = await client.makeRequestWithRetry('/interviews', {
      method: 'POST',
      body: JSON.stringify(interviewData)
    });
    console.log('Interview created successfully:', interview.data.id);
  } catch (error) {
    console.error('All retry attempts failed:', error.message);
  }
  ```

  ```python Python Retry Logic theme={null}
  import time
  import random
  from typing import Dict, Any, Optional
  import requests

  class RetryableJobHiveClient:
      def __init__(self, api_key: str, max_retries: int = 3):
          self.api_key = api_key
          self.max_retries = max_retries
          self.base_url = 'https://backend.jobhive.ai/v1'
      
      def make_request_with_retry(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
          url = f"{self.base_url}/{endpoint.lstrip('/')}"
          
          headers = kwargs.get('headers', {})
          headers.update({
              'Authorization': f'Bearer {self.api_key}',
              'Content-Type': 'application/json'
          })
          kwargs['headers'] = headers
          
          last_exception = None
          
          for attempt in range(1, self.max_retries + 1):
              try:
                  response = requests.request(method, url, **kwargs)
                  data = response.json() if response.content else {}
                  
                  if response.ok:
                      return data
                  
                  # Check if error is retryable
                  if self.is_retryable_error(response.status_code) and attempt < self.max_retries:
                      delay = self.calculate_delay(attempt, response.headers)
                      print(f"Attempt {attempt} failed (HTTP {response.status_code}), retrying in {delay}s...")
                      time.sleep(delay)
                      continue
                  
                  # Non-retryable error or max attempts reached
                  raise JobHiveAPIError(response.status_code, data.get('error', data))
                  
              except requests.RequestException as e:
                  last_exception = e
                  
                  if attempt < self.max_retries:
                      delay = self.calculate_delay(attempt)
                      print(f"Network error on attempt {attempt}, retrying in {delay}s...")
                      time.sleep(delay)
                      continue
                  
                  # Max attempts reached
                  break
          
          # All attempts failed
          raise Exception(f"Request failed after {self.max_retries} attempts: {last_exception}")
      
      def is_retryable_error(self, status_code: int) -> bool:
          """Check if error status code is retryable"""
          return status_code >= 500 or status_code == 429
      
      def calculate_delay(self, attempt: int, headers: Optional[Dict] = None) -> float:
          """Calculate delay with exponential backoff and jitter"""
          # Use Retry-After header if available
          if headers and 'X-RateLimit-Retry-After' in headers:
              return float(headers['X-RateLimit-Retry-After'])
          
          # Exponential backoff with jitter
          base_delay = min(2 ** (attempt - 1), 30)  # Cap at 30 seconds
          jitter = random.uniform(0.1, 0.5)  # Add random jitter
          return base_delay + jitter

  # Usage
  client = RetryableJobHiveClient(os.environ['JOBHIVE_API_KEY'], max_retries=3)

  try:
      interview = client.make_request_with_retry('POST', '/interviews', json={
          'candidate_email': 'test@example.com',
          'position': 'Software Engineer',
          'skills': ['Python', 'Django']
      })
      print(f"Interview created: {interview['data']['id']}")
      
  except JobHiveAPIError as e:
      print(f"API Error: {e.message}")
      if e.code == 'VALIDATION_ERROR':
          print("Please check your input data:", e.details)
      elif e.code == 'RATE_LIMIT_EXCEEDED':
          print("Rate limit exceeded. Please slow down your requests.")
          
  except Exception as e:
      print(f"Request failed: {e}")
  ```
</CodeGroup>

### Circuit Breaker Pattern

Implement a circuit breaker to handle sustained failures gracefully:

<CodeGroup>
  ```javascript Circuit Breaker theme={null}
  class CircuitBreaker {
    constructor(threshold = 5, timeout = 60000) {
      this.threshold = threshold; // Number of failures before opening
      this.timeout = timeout; // Time to wait before trying again
      this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
      this.failureCount = 0;
      this.lastFailureTime = null;
    }

    async execute(fn) {
      if (this.state === 'OPEN') {
        if (Date.now() - this.lastFailureTime > this.timeout) {
          this.state = 'HALF_OPEN';
        } else {
          throw new Error('Circuit breaker is OPEN');
        }
      }

      try {
        const result = await fn();
        this.onSuccess();
        return result;
      } catch (error) {
        this.onFailure();
        throw error;
      }
    }

    onSuccess() {
      this.failureCount = 0;
      this.state = 'CLOSED';
    }

    onFailure() {
      this.failureCount++;
      this.lastFailureTime = Date.now();

      if (this.failureCount >= this.threshold) {
        this.state = 'OPEN';
        console.log('Circuit breaker opened due to failures');
      }
    }
  }

  // Usage with JobHive client
  const circuitBreaker = new CircuitBreaker(3, 30000);
  const client = new RetryableJobHiveClient(process.env.JOBHIVE_API_KEY);

  async function createInterviewSafely(data) {
    return circuitBreaker.execute(async () => {
      return client.makeRequestWithRetry('/interviews', {
        method: 'POST',
        body: JSON.stringify(data)
      });
    });
  }

  // This will fail fast if too many errors occur
  try {
    const interview = await createInterviewSafely(interviewData);
    console.log('Interview created:', interview.data.id);
  } catch (error) {
    if (error.message === 'Circuit breaker is OPEN') {
      console.log('Service temporarily unavailable, try again later');
    } else {
      console.error('Interview creation failed:', error.message);
    }
  }
  ```

  ```python Circuit Breaker Python theme={null}
  import time
  from enum import Enum
  from typing import Callable, Any

  class CircuitState(Enum):
      CLOSED = "CLOSED"
      OPEN = "OPEN"
      HALF_OPEN = "HALF_OPEN"

  class CircuitBreaker:
      def __init__(self, failure_threshold: int = 5, timeout: int = 60):
          self.failure_threshold = failure_threshold
          self.timeout = timeout
          self.state = CircuitState.CLOSED
          self.failure_count = 0
          self.last_failure_time = None
      
      def __call__(self, func: Callable) -> Callable:
          def wrapper(*args, **kwargs):
              return self.execute(lambda: func(*args, **kwargs))
          return wrapper
      
      def execute(self, func: Callable) -> Any:
          if self.state == CircuitState.OPEN:
              if time.time() - self.last_failure_time > self.timeout:
                  self.state = CircuitState.HALF_OPEN
                  print("Circuit breaker moving to HALF_OPEN state")
              else:
                  raise Exception("Circuit breaker is OPEN - service unavailable")
          
          try:
              result = func()
              self.on_success()
              return result
          except Exception as e:
              self.on_failure()
              raise e
      
      def on_success(self):
          self.failure_count = 0
          if self.state == CircuitState.HALF_OPEN:
              self.state = CircuitState.CLOSED
              print("Circuit breaker returned to CLOSED state")
      
      def on_failure(self):
          self.failure_count += 1
          self.last_failure_time = time.time()
          
          if self.failure_count >= self.failure_threshold:
              self.state = CircuitState.OPEN
              print(f"Circuit breaker OPENED after {self.failure_count} failures")

  # Usage
  circuit_breaker = CircuitBreaker(failure_threshold=3, timeout=30)
  client = RetryableJobHiveClient(os.environ['JOBHIVE_API_KEY'])

  @circuit_breaker
  def create_interview_protected(interview_data):
      return client.make_request_with_retry('POST', '/interviews', json=interview_data)

  # Protected function will fail fast if service is down
  try:
      interview = create_interview_protected({
          'candidate_email': 'test@example.com',
          'position': 'Software Engineer',
          'skills': ['Python', 'Django']
      })
      print(f"Interview created: {interview['data']['id']}")
      
  except Exception as e:
      if "Circuit breaker is OPEN" in str(e):
          print("JobHive service temporarily unavailable")
      else:
          print(f"Interview creation failed: {e}")
  ```
</CodeGroup>

## Error Recovery Strategies

### Graceful Degradation

<AccordionGroup>
  <Accordion title="Fallback to Manual Process">
    ```javascript theme={null}
    async function createInterviewWithFallback(candidateData) {
      try {
        // Try automated interview creation
        const interview = await jobhiveClient.createInterview(candidateData);
        return { success: true, type: 'automated', interview };
      } catch (error) {
        console.warn('Automated interview failed, falling back to manual process');
        
        // Log for manual follow-up
        await logManualInterviewRequest(candidateData, error);
        
        // Send notification to hiring team
        await notifyHiringTeam({
          type: 'manual_interview_required',
          candidate: candidateData,
          reason: error.message
        });
        
        return { 
          success: true, 
          type: 'manual', 
          message: 'Interview scheduled for manual processing' 
        };
      }
    }
    ```
  </Accordion>

  <Accordion title="Queue for Later Processing">
    ```python theme={null}
    from collections import deque
    import json

    class InterviewQueue:
        def __init__(self):
            self.queue = deque()
            self.failed_queue = deque()
        
        def add_interview(self, interview_data):
            self.queue.append(interview_data)
        
        def process_queue(self, client):
            while self.queue:
                interview_data = self.queue.popleft()
                
                try:
                    result = client.make_request_with_retry('POST', '/interviews', 
                                                          json=interview_data)
                    print(f"✅ Successfully created interview: {result['data']['id']}")
                    
                except JobHiveAPIError as e:
                    if e.code in ['RATE_LIMIT_EXCEEDED', 'SERVICE_UNAVAILABLE']:
                        # Put back in queue for retry
                        self.queue.appendleft(interview_data)
                        print(f"⏳ Rate limited, pausing processing...")
                        break
                    else:
                        # Permanent failure
                        self.failed_queue.append({
                            'data': interview_data,
                            'error': {'code': e.code, 'message': e.message}
                        })
                        print(f"❌ Permanent failure: {e.message}")
        
        def export_failed_interviews(self, filename):
            with open(filename, 'w') as f:
                json.dump(list(self.failed_queue), f, indent=2)
            print(f"💾 Exported {len(self.failed_queue)} failed interviews to {filename}")

    # Usage
    queue = InterviewQueue()

    # Add interviews to queue
    for candidate in candidate_list:
        queue.add_interview({
            'candidate_email': candidate['email'],
            'position': candidate['position'],
            'skills': candidate['skills']
        })

    # Process with error handling
    try:
        queue.process_queue(client)
    except Exception as e:
        print(f"Queue processing stopped: {e}")
        queue.export_failed_interviews('failed_interviews.json')
    ```
  </Accordion>
</AccordionGroup>

### Error Monitoring and Alerting

<CodeGroup>
  ```javascript Error Monitoring theme={null}
  class ErrorMonitor {
    constructor() {
      this.errorCounts = new Map();
      this.alertThresholds = {
        'RATE_LIMIT_EXCEEDED': 5,
        'VALIDATION_ERROR': 10,
        'SERVICE_UNAVAILABLE': 3
      };
    }

    recordError(error) {
      const errorCode = error.code || 'UNKNOWN_ERROR';
      const count = this.errorCounts.get(errorCode) || 0;
      this.errorCounts.set(errorCode, count + 1);

      // Check if we need to send an alert
      const threshold = this.alertThresholds[errorCode] || 20;
      if (count + 1 >= threshold) {
        this.sendAlert(errorCode, count + 1);
      }

      // Log error details
      console.error(`Error recorded: ${errorCode} (count: ${count + 1})`);
      console.error('Error details:', error);
    }

    async sendAlert(errorCode, count) {
      const alert = {
        service: 'JobHive API',
        error_code: errorCode,
        count: count,
        timestamp: new Date().toISOString(),
        severity: this.getSeverity(errorCode)
      };

      // Send to monitoring service (e.g., PagerDuty, Slack)
      await this.notifyOpsTeam(alert);
    }

    getSeverity(errorCode) {
      const highSeverity = ['SERVICE_UNAVAILABLE', 'AUTHENTICATION_REQUIRED'];
      return highSeverity.includes(errorCode) ? 'HIGH' : 'MEDIUM';
    }

    async notifyOpsTeam(alert) {
      // Example: Send to Slack webhook
      try {
        await fetch(process.env.SLACK_WEBHOOK_URL, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            text: `🚨 JobHive API Alert: ${alert.error_code}`,
            attachments: [{
              color: alert.severity === 'HIGH' ? 'danger' : 'warning',
              fields: [
                { title: 'Error Code', value: alert.error_code, short: true },
                { title: 'Count', value: alert.count, short: true },
                { title: 'Severity', value: alert.severity, short: true },
                { title: 'Time', value: alert.timestamp, short: true }
              ]
            }]
          })
        });
      } catch (e) {
        console.error('Failed to send alert:', e);
      }
    }

    getErrorReport() {
      return {
        total_errors: Array.from(this.errorCounts.values()).reduce((a, b) => a + b, 0),
        error_breakdown: Object.fromEntries(this.errorCounts),
        timestamp: new Date().toISOString()
      };
    }
  }

  // Usage
  const errorMonitor = new ErrorMonitor();

  try {
    const interview = await createInterview(data);
  } catch (error) {
    errorMonitor.recordError(error);
    throw error; // Re-throw for local handling
  }
  ```

  ```python Error Analytics theme={null}
  import logging
  from datetime import datetime, timedelta
  from collections import defaultdict, Counter
  from typing import Dict, List
  import json

  class ErrorAnalytics:
      def __init__(self):
          self.error_log = []
          self.error_counts = Counter()
          self.hourly_errors = defaultdict(int)
          
          # Setup logging
          logging.basicConfig(
              level=logging.INFO,
              format='%(asctime)s - %(levelname)s - %(message)s',
              handlers=[
                  logging.FileHandler('jobhive_errors.log'),
                  logging.StreamHandler()
              ]
          )
          self.logger = logging.getLogger(__name__)
      
      def log_error(self, error: Exception, context: Dict = None):
          """Log error with context and analytics"""
          error_data = {
              'timestamp': datetime.now().isoformat(),
              'error_type': type(error).__name__,
              'error_message': str(error),
              'context': context or {}
          }
          
          # Add API-specific data if available
          if hasattr(error, 'code'):
              error_data['api_error_code'] = error.code
              error_data['api_status_code'] = getattr(error, 'status_code', None)
          
          self.error_log.append(error_data)
          
          # Update counters
          error_key = error_data.get('api_error_code', error_data['error_type'])
          self.error_counts[error_key] += 1
          
          # Track hourly patterns
          hour_key = datetime.now().strftime('%Y-%m-%d %H:00')
          self.hourly_errors[hour_key] += 1
          
          # Log to file
          self.logger.error(f"JobHive API Error: {error_key}", extra=error_data)
          
          # Check for patterns that need attention
          self.analyze_error_patterns()
      
      def analyze_error_patterns(self):
          """Analyze recent errors for concerning patterns"""
          recent_errors = [
              e for e in self.error_log 
              if datetime.fromisoformat(e['timestamp']) > datetime.now() - timedelta(hours=1)
          ]
          
          if len(recent_errors) > 10:
              self.logger.warning(f"High error rate: {len(recent_errors)} errors in the last hour")
          
          # Check for specific error spikes
          recent_codes = Counter(e.get('api_error_code', e['error_type']) for e in recent_errors)
          for code, count in recent_codes.items():
              if count > 5:
                  self.logger.warning(f"Error spike detected: {code} occurred {count} times in the last hour")
      
      def generate_error_report(self, hours: int = 24) -> Dict:
          """Generate comprehensive error report"""
          cutoff_time = datetime.now() - timedelta(hours=hours)
          recent_errors = [
              e for e in self.error_log 
              if datetime.fromisoformat(e['timestamp']) > cutoff_time
          ]
          
          return {
              'report_period': f"Last {hours} hours",
              'total_errors': len(recent_errors),
              'error_types': dict(Counter(e.get('api_error_code', e['error_type']) for e in recent_errors)),
              'hourly_distribution': dict(self.hourly_errors),
              'top_error_contexts': self.get_top_error_contexts(recent_errors),
              'recommendations': self.get_recommendations(recent_errors)
          }
      
      def get_top_error_contexts(self, errors: List[Dict]) -> List[Dict]:
          """Find common contexts in errors"""
          contexts = []
          for error in errors:
              if error.get('context'):
                  contexts.append(error['context'])
          
          # Group by similar contexts (simplified)
          return contexts[:5]  # Return top 5
      
      def get_recommendations(self, errors: List[Dict]) -> List[str]:
          """Generate recommendations based on error patterns"""
          recommendations = []
          error_codes = Counter(e.get('api_error_code') for e in errors)
          
          if error_codes.get('RATE_LIMIT_EXCEEDED', 0) > 5:
              recommendations.append("Consider implementing request queuing or reducing API call frequency")
          
          if error_codes.get('VALIDATION_ERROR', 0) > 3:
              recommendations.append("Review input validation logic before making API calls")
          
          if error_codes.get('AUTHENTICATION_REQUIRED', 0) > 0:
              recommendations.append("Check API key configuration and rotation schedule")
          
          return recommendations
      
      def export_error_data(self, filename: str):
          """Export error data for external analysis"""
          with open(filename, 'w') as f:
              json.dump(self.error_log, f, indent=2, default=str)
          
          print(f"📊 Exported {len(self.error_log)} error records to {filename}")

  # Usage
  error_analytics = ErrorAnalytics()

  try:
      interview = client.make_request_with_retry('POST', '/interviews', json=data)
  except JobHiveAPIError as e:
      error_analytics.log_error(e, context={
          'operation': 'create_interview',
          'candidate_email': data.get('candidate_email'),
          'position': data.get('position')
      })
      raise
  except Exception as e:
      error_analytics.log_error(e, context={'operation': 'create_interview'})
      raise

  # Generate daily report
  report = error_analytics.generate_error_report(24)
  print("📈 Error Report:", json.dumps(report, indent=2))
  ```
</CodeGroup>

## Best Practices Summary

<CardGroup cols={2}>
  <Card title="Proactive Error Handling" icon="shield-check">
    **Implementation Checklist**

    * Validate input data before API calls
    * Implement retry logic with exponential backoff
    * Use circuit breakers for sustained failures
    * Monitor error rates and patterns
  </Card>

  <Card title="User Experience" icon="user-check">
    **UX Considerations**

    * Provide clear error messages to users
    * Implement graceful degradation
    * Show loading states during retries
    * Offer alternative actions when possible
  </Card>

  <Card title="Monitoring & Alerting" icon="bell">
    **Observability Setup**

    * Log all errors with context
    * Set up alerts for critical errors
    * Track error trends over time
    * Generate regular error reports
  </Card>

  <Card title="Recovery Strategies" icon="arrows-rotate">
    **Resilience Patterns**

    * Queue failed requests for retry
    * Implement manual fallback processes
    * Use multiple API keys for redundancy
    * Plan for maintenance windows
  </Card>
</CardGroup>

<Note>
  **Error Prevention**: The best error handling strategy is preventing errors in the first place. Always validate input data, test thoroughly, and monitor your integration continuously.
</Note>

<Tip>
  **Support Resources**: For persistent issues, contact our support team at [dev-exec@jobhive.ai](mailto:dev-exec@jobhive.ai) with your request IDs and error logs for faster resolution.
</Tip>
