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

# Error Handling

> Comprehensive guide to handling API errors and implementing robust error recovery

# Error Handling

This guide covers error handling strategies, common error types, and best practices for building resilient applications with the Blue Oyster API.

## Error Response Format

All API errors follow this standard format:

```json theme={null}
{
  "error": "Human-readable error message",
  "stack": "Stack trace (development only)",
  "code": "MACHINE_READABLE_ERROR_CODE",
  "details": {
    "additional": "contextual information"
  }
}
```

## HTTP Status Codes

| Code  | Description                                    | Retry              |
| ----- | ---------------------------------------------- | ------------------ |
| `400` | Bad Request - Invalid parameters               | No                 |
| `401` | Unauthorized - Authentication required         | No                 |
| `403` | Forbidden - Insufficient permissions           | No                 |
| `404` | Not Found - Endpoint or resource doesn't exist | No                 |
| `429` | Too Many Requests - Rate limited               | Yes (with backoff) |
| `500` | Internal Server Error - Server-side issue      | Yes                |
| `502` | Bad Gateway - Upstream service error           | Yes                |
| `503` | Service Unavailable - Temporary outage         | Yes                |
| `504` | Gateway Timeout - Request timeout              | Yes                |

## Common Error Codes

### API Errors

<ResponseField name="TOOL_EXECUTION_FAILED" type="string">
  Tool execution encountered an error. Check tool parameters and external service status.
</ResponseField>

<ResponseField name="MEMORY_NOT_INITIALIZED" type="string">
  MongoDB memory system not configured. Thread persistence unavailable.
</ResponseField>

<ResponseField name="INVALID_REQUEST" type="string">
  Request body malformed or missing required parameters.
</ResponseField>

<ResponseField name="RATE_LIMITED" type="string">
  Too many requests. Implement exponential backoff.
</ResponseField>

<ResponseField name="MODEL_ERROR" type="string">
  AI model processing failed. May be temporary.
</ResponseField>

### Speech-to-Text Errors

<ResponseField name="AUDIO_PROCESSING_ERROR" type="string">
  Audio file processing failed. Check file format and size.
</ResponseField>

<ResponseField name="INVALID_AUDIO_FORMAT" type="string">
  Audio file format not supported.
</ResponseField>

<ResponseField name="AUDIO_TOO_LARGE" type="string">
  Audio file exceeds size limit (25MB).
</ResponseField>

## Error Handling Strategies

### Basic Error Handling

```javascript theme={null}
async function makeAPIRequest(endpoint, data) {
  try {
    const response = await fetch(`${BASE_URL}${endpoint}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });

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

    return await response.json();
  } catch (error) {
    console.error('Request failed:', error);
    throw error;
  }
}
```

### Exponential Backoff

```javascript theme={null}
class RetryHandler {
  constructor(maxRetries = 3, baseDelay = 1000) {
    this.maxRetries = maxRetries;
    this.baseDelay = baseDelay;
  }

  async executeWithRetry(operation) {
    let lastError;

    for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
      try {
        return await operation();
      } catch (error) {
        lastError = error;

        if (!this.shouldRetry(error) || attempt === this.maxRetries) {
          throw error;
        }

        const delay = this.calculateDelay(attempt);
        await this.sleep(delay);
      }
    }

    throw lastError;
  }

  shouldRetry(error) {
    // Retry on network errors and server errors
    if (error.name === 'TypeError' || error.message.includes('fetch')) {
      return true; // Network error
    }

    // Retry on specific HTTP status codes
    const retryableStatuses = [429, 500, 502, 503, 504];
    return retryableStatuses.some(status =>
      error.message.includes(status.toString())
    );
  }

  calculateDelay(attempt) {
    // Exponential backoff with jitter
    const exponentialDelay = this.baseDelay * Math.pow(2, attempt);
    const jitter = Math.random() * 0.1 * exponentialDelay;
    return exponentialDelay + jitter;
  }

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

// Usage
const retryHandler = new RetryHandler();

const result = await retryHandler.executeWithRetry(async () => {
  return await chat.sendMessage("Hello!", "friend");
});
```

### Circuit Breaker Pattern

```javascript theme={null}
class CircuitBreaker {
  constructor(failureThreshold = 5, recoveryTimeout = 60000) {
    this.failureThreshold = failureThreshold;
    this.recoveryTimeout = recoveryTimeout;
    this.failureCount = 0;
    this.lastFailureTime = null;
    this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
  }

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

    try {
      const result = await operation();
      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.failureThreshold) {
      this.state = 'OPEN';
    }
  }

  getState() {
    return this.state;
  }
}

// Usage
const circuitBreaker = new CircuitBreaker();

const result = await circuitBreaker.execute(async () => {
  return await chat.sendMessage("Hello!", "friend");
});
```

### Comprehensive Error Handler

```javascript theme={null}
class APIErrorHandler {
  constructor(chat) {
    this.chat = chat;
    this.retryHandler = new RetryHandler();
    this.circuitBreaker = new CircuitBreaker();
  }

  async sendMessageWithErrorHandling(message, personality = 'friend') {
    try {
      return await this.circuitBreaker.execute(async () => {
        return await this.retryHandler.executeWithRetry(async () => {
          return await this.chat.sendMessage(message, personality);
        });
      });
    } catch (error) {
      return await this.handleError(error, message, personality);
    }
  }

  async handleError(error, originalMessage, personality) {
    const errorType = this.categorizeError(error);

    switch (errorType) {
      case 'network':
        return await this.handleNetworkError(originalMessage, personality);

      case 'rate_limit':
        return await this.handleRateLimit(originalMessage, personality);

      case 'server_error':
        return await this.handleServerError(originalMessage, personality);

      case 'validation':
        return await this.handleValidationError(error, originalMessage);

      default:
        return await this.handleUnknownError(error, originalMessage);
    }
  }

  categorizeError(error) {
    const message = error.message.toLowerCase();

    if (message.includes('network') || message.includes('fetch')) {
      return 'network';
    }
    if (message.includes('429') || message.includes('rate')) {
      return 'rate_limit';
    }
    if (message.includes('500') || message.includes('502') || message.includes('503')) {
      return 'server_error';
    }
    if (message.includes('400') || message.includes('invalid')) {
      return 'validation';
    }
    return 'unknown';
  }

  async handleNetworkError(message, personality) {
    // Try with simplified message
    const simplifiedMessage = message.length > 100 ?
      message.substring(0, 100) + '...' : message;

    try {
      return await this.chat.sendMessage(simplifiedMessage, personality);
    } catch (fallbackError) {
      throw new Error('Network connectivity issues. Please check your connection and try again.');
    }
  }

  async handleRateLimit(message, personality) {
    // Wait and retry once
    await this.sleep(2000);
    try {
      return await this.chat.sendMessage(message, personality);
    } catch (retryError) {
      throw new Error('Rate limit exceeded. Please wait a moment before trying again.');
    }
  }

  async handleServerError(message, personality) {
    // Try with different personality or simplified request
    const fallbackPersonality = personality === 'guru' ? 'friend' : 'guru';

    try {
      return await this.chat.sendMessage(message, fallbackPersonality);
    } catch (fallbackError) {
      throw new Error('Service temporarily unavailable. Please try again in a few minutes.');
    }
  }

  async handleValidationError(error, message) {
    // Extract validation details and provide helpful feedback
    const details = error.details || {};
    let helpfulMessage = 'Request validation failed. ';

    if (details.missingField) {
      helpfulMessage += `Missing required field: ${details.missingField}`;
    } else if (details.invalidField) {
      helpfulMessage += `Invalid field: ${details.invalidField}`;
    } else {
      helpfulMessage += 'Please check your request parameters.';
    }

    throw new Error(helpfulMessage);
  }

  async handleUnknownError(error, message) {
    // Log for debugging and provide generic message
    console.error('Unknown API error:', error);
    throw new Error('An unexpected error occurred. Please try again or contact support if the issue persists.');
  }

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

// Usage
const errorHandler = new APIErrorHandler(chat);

const result = await errorHandler.sendMessageWithErrorHandling(
  "Tell me about meditation",
  "friend"
);
```

## Error Monitoring

### Error Tracking

```javascript theme={null}
class ErrorTracker {
  constructor() {
    this.errors = [];
    this.maxErrors = 100;
  }

  trackError(error, context = {}) {
    const errorEntry = {
      timestamp: new Date().toISOString(),
      error: error.message,
      stack: error.stack,
      context,
      userAgent: navigator.userAgent,
      url: window.location.href
    };

    this.errors.push(errorEntry);

    // Keep only recent errors
    if (this.errors.length > this.maxErrors) {
      this.errors.shift();
    }

    // Send to error reporting service
    this.reportError(errorEntry);
  }

  reportError(errorEntry) {
    // Send to your error reporting service
    console.error('API Error:', errorEntry);

    // Example: Send to analytics service
    if (window.gtag) {
      window.gtag('event', 'exception', {
        description: errorEntry.error,
        fatal: false
      });
    }
  }

  getErrorSummary() {
    const summary = {
      totalErrors: this.errors.length,
      recentErrors: this.errors.slice(-10),
      errorTypes: this.categorizeErrors()
    };

    return summary;
  }

  categorizeErrors() {
    const categories = {};

    this.errors.forEach(error => {
      const category = this.categorizeError(error.error);
      categories[category] = (categories[category] || 0) + 1;
    });

    return categories;
  }

  categorizeError(errorMessage) {
    const message = errorMessage.toLowerCase();

    if (message.includes('network') || message.includes('fetch')) {
      return 'Network';
    }
    if (message.includes('rate') || message.includes('429')) {
      return 'Rate Limit';
    }
    if (message.includes('500') || message.includes('server')) {
      return 'Server Error';
    }
    if (message.includes('400') || message.includes('invalid')) {
      return 'Validation';
    }
    return 'Other';
  }
}

// Usage
const errorTracker = new ErrorTracker();

// Wrap API calls with error tracking
try {
  const result = await chat.sendMessage("Hello!", "friend");
} catch (error) {
  errorTracker.trackError(error, {
    action: 'send_message',
    personality: 'friend',
    messageLength: 6
  });

  // Handle error...
}
```

## Best Practices

<Tip>
  **Always implement error boundaries** in your UI to prevent crashes.
</Tip>

<Tip>
  **Provide user-friendly error messages** instead of raw API errors.
</Tip>

<Tip>
  **Implement graceful degradation** when services are unavailable.
</Tip>

<Tip>
  **Log errors for debugging** but don't expose sensitive information.
</Tip>

<Tip>
  **Use exponential backoff** for retry logic to avoid overwhelming services.
</Tip>

<Tip>
  **Monitor error rates** and implement circuit breakers for resilience.
</Tip>
