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

# Error Handling Best Practices

> Comprehensive guide to handling errors gracefully in StateSet API integrations

# Error Handling Best Practices

Robust error handling is critical for production applications. This guide covers comprehensive strategies for handling StateSet API errors gracefully, implementing retry logic, and maintaining application reliability.

## Understanding StateSet Error Responses

### Error Response Structure

All StateSet API errors follow a consistent structure:

```json theme={null}
{
  "error": {
    "type": "invalid_request_error",
    "code": "INVALID_PARAMETER",
    "message": "The 'email' parameter is required",
    "param": "email",
    "request_id": "req_abc123",
    "documentation_url": "https://docs.StateSet.com/api-reference/errors#invalid-parameter"
  },
  "timestamp": "2024-01-15T10:30:00Z"
}
```

### Error Categories

<CardGroup cols={2}>
  <Card title="Client Errors (4xx)" icon="user-xmark">
    **Errors in your request**

    * Authentication failures
    * Invalid parameters
    * Rate limiting
    * Resource not found
  </Card>

  <Card title="Server Errors (5xx)" icon="server">
    **Temporary service issues**

    * Internal server errors
    * Service unavailable
    * Gateway timeouts
    * Maintenance mode
  </Card>
</CardGroup>

## Common Error Codes

### Authentication Errors

<Tabs>
  <Tab title="UNAUTHORIZED (401)">
    **Cause**: Invalid or missing API key

    ```javascript theme={null}
    // ❌ Common mistake
    const client = new StateSetClient({
      apiKey: 'wrong_key_format'
    });

    // ✅ Proper handling
    const client = new StateSetClient({
      apiKey: process.env.STATESET_API_KEY
    });

    if (!process.env.STATESET_API_KEY) {
      throw new Error('STATESET_API_KEY environment variable is required');
    }

    try {
      const customers = await client.customers.list();
    } catch (error) {
      if (error.code === 'UNAUTHORIZED') {
        logger.error('Authentication failed - check API key', {
          requestId: error.request_id,
          documentation: error.documentation_url
        });
        // Notify operations team
        await notifyAuthenticationFailure(error);
      }
      throw error;
    }
    ```
  </Tab>

  <Tab title="FORBIDDEN (403)">
    **Cause**: API key lacks required permissions

    ```javascript theme={null}
    try {
      const customer = await client.customers.delete(customerId);
    } catch (error) {
      if (error.code === 'FORBIDDEN') {
        logger.warn('Insufficient permissions for operation', {
          operation: 'customer.delete',
          customerId,
          requiredPermission: 'customers:write',
          requestId: error.request_id
        });
        
        // Fallback to read-only operation
        const customer = await client.customers.get(customerId);
        return { customer, canDelete: false };
      }
      throw error;
    }
    ```
  </Tab>
</Tabs>

### Validation Errors

```javascript theme={null}
// ✅ Comprehensive validation error handling
async function createCustomerSafely(customerData) {
  try {
    const customer = await client.customers.create(customerData);
    logger.info('Customer created successfully', { customerId: customer.id });
    return customer;
  } catch (error) {
    if (error.code === 'INVALID_PARAMETER') {
      const validationErrors = error.details?.validation_errors || [];
      
      logger.warn('Customer creation failed - validation errors', {
        errors: validationErrors,
        requestId: error.request_id,
        inputData: sanitizeForLogging(customerData)
      });
      
      // Return user-friendly error messages
      return {
        success: false,
        errors: validationErrors.map(err => ({
          field: err.param,
          message: err.message,
          code: err.code
        }))
      };
    }
    
    if (error.code === 'DUPLICATE_RESOURCE') {
      logger.info('Customer already exists', { 
        email: customerData.email,
        existingId: error.details?.existing_resource_id
      });
      
      // Return existing customer
      return await client.customers.get(error.details.existing_resource_id);
    }
    
    throw error; // Re-throw unexpected errors
  }
}

// Helper function to sanitize sensitive data
function sanitizeForLogging(data) {
  const sanitized = { ...data };
  if (sanitized.email) sanitized.email = maskEmail(sanitized.email);
  if (sanitized.phone) sanitized.phone = maskPhone(sanitized.phone);
  return sanitized;
}
```

### Rate Limiting

```javascript theme={null}
// ✅ Intelligent rate limit handling with exponential backoff
class RateLimitHandler {
  constructor(options = {}) {
    this.maxRetries = options.maxRetries || 3;
    this.baseDelay = options.baseDelay || 1000;
    this.maxDelay = options.maxDelay || 30000;
    this.jitterFactor = options.jitterFactor || 0.1;
  }
  
  async executeWithRetry(operation, context = {}) {
    let lastError;
    
    for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
      try {
        return await operation();
      } catch (error) {
        lastError = error;
        
        if (error.code !== 'RATE_LIMIT_EXCEEDED') {
          throw error; // Not a rate limit error
        }
        
        if (attempt === this.maxRetries) {
          logger.error('Max retries exceeded for rate limited request', {
            attempts: attempt + 1,
            context,
            requestId: error.request_id
          });
          throw error;
        }
        
        const delay = this.calculateDelay(attempt, error);
        logger.warn('Rate limited - retrying request', {
          attempt: attempt + 1,
          delayMs: delay,
          retryAfter: error.headers?.['retry-after'],
          context,
          requestId: error.request_id
        });
        
        await this.sleep(delay);
      }
    }
    
    throw lastError;
  }
  
  calculateDelay(attempt, error) {
    // Use server-provided retry-after if available
    const retryAfter = error.headers?.['retry-after'];
    if (retryAfter) {
      return Math.min(parseInt(retryAfter) * 1000, this.maxDelay);
    }
    
    // Exponential backoff with jitter
    const exponentialDelay = this.baseDelay * Math.pow(2, attempt);
    const jitter = exponentialDelay * this.jitterFactor * Math.random();
    return Math.min(exponentialDelay + jitter, this.maxDelay);
  }
  
  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// Usage example
const rateLimitHandler = new RateLimitHandler({
  maxRetries: 5,
  baseDelay: 1000,
  maxDelay: 60000
});

async function createCustomerWithRetry(customerData) {
  return await rateLimitHandler.executeWithRetry(
    () => client.customers.create(customerData),
    { operation: 'customer.create', email: customerData.email }
  );
}
```

## Advanced Error Handling Patterns

### Circuit Breaker Pattern

```javascript theme={null}
// ✅ Circuit breaker to prevent cascading failures
class CircuitBreaker {
  constructor(options = {}) {
    this.failureThreshold = options.failureThreshold || 5;
    this.timeout = options.timeout || 60000;
    this.monitoringPeriod = options.monitoringPeriod || 10000;
    
    this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
    this.failureCount = 0;
    this.lastFailureTime = null;
    this.nextAttempt = null;
  }
  
  async execute(operation, fallback = null) {
    if (this.state === 'OPEN') {
      if (Date.now() < this.nextAttempt) {
        logger.warn('Circuit breaker is OPEN - using fallback', {
          nextAttempt: new Date(this.nextAttempt).toISOString(),
          failureCount: this.failureCount
        });
        
        if (fallback) return await fallback();
        throw new Error('Circuit breaker is OPEN - service unavailable');
      }
      
      this.state = 'HALF_OPEN';
      logger.info('Circuit breaker entering HALF_OPEN state');
    }
    
    try {
      const result = await operation();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }
  
  onSuccess() {
    this.failureCount = 0;
    this.state = 'CLOSED';
    logger.info('Circuit breaker reset to CLOSED state');
  }
  
  onFailure() {
    this.failureCount++;
    this.lastFailureTime = Date.now();
    
    if (this.failureCount >= this.failureThreshold) {
      this.state = 'OPEN';
      this.nextAttempt = Date.now() + this.timeout;
      
      logger.error('Circuit breaker opened due to failures', {
        failureCount: this.failureCount,
        nextAttempt: new Date(this.nextAttempt).toISOString()
      });
    }
  }
}

// Usage with StateSet API
const circuitBreaker = new CircuitBreaker({
  failureThreshold: 3,
  timeout: 30000
});

async function getCustomerWithFallback(customerId) {
  return await circuitBreaker.execute(
    () => client.customers.get(customerId),
    () => getCachedCustomer(customerId) // Fallback to cache
  );
}
```

### Batch Operations Error Handling

```javascript theme={null}
// ✅ Robust batch processing with partial failure handling
class BatchProcessor {
  constructor(options = {}) {
    this.batchSize = options.batchSize || 100;
    this.maxConcurrency = options.maxConcurrency || 5;
    this.retryFailures = options.retryFailures !== false;
  }
  
  async processBatch(items, processor) {
    const batches = this.createBatches(items);
    const results = [];
    const failures = [];
    
    for (const batch of batches) {
      try {
        const batchResults = await this.processBatchConcurrently(batch, processor);
        results.push(...batchResults.successes);
        failures.push(...batchResults.failures);
      } catch (error) {
        logger.error('Batch processing failed completely', {
          batchSize: batch.length,
          error: error.message
        });
        
        // Add all items in failed batch to failures
        failures.push(...batch.map(item => ({
          item,
          error: error.message,
          retryable: this.isRetryableError(error)
        })));
      }
    }
    
    // Retry failures if enabled
    if (this.retryFailures && failures.length > 0) {
      const retryableFailures = failures.filter(f => f.retryable);
      if (retryableFailures.length > 0) {
        logger.info('Retrying failed batch items', {
          retryCount: retryableFailures.length,
          totalFailures: failures.length
        });
        
        const retryResults = await this.retryFailures(retryableFailures, processor);
        results.push(...retryResults.successes);
        // Update failures list with remaining failures
        const remainingFailures = failures.filter(f => !f.retryable);
        remainingFailures.push(...retryResults.failures);
      }
    }
    
    return {
      successes: results,
      failures,
      summary: {
        total: items.length,
        succeeded: results.length,
        failed: failures.length,
        successRate: (results.length / items.length) * 100
      }
    };
  }
  
  createBatches(items) {
    const batches = [];
    for (let i = 0; i < items.length; i += this.batchSize) {
      batches.push(items.slice(i, i + this.batchSize));
    }
    return batches;
  }
  
  async processBatchConcurrently(batch, processor) {
    const semaphore = new Semaphore(this.maxConcurrency);
    const successes = [];
    const failures = [];
    
    const promises = batch.map(async (item, index) => {
      await semaphore.acquire();
      try {
        const result = await processor(item, index);
        successes.push({ item, result });
      } catch (error) {
        failures.push({
          item,
          error: error.message,
          code: error.code,
          retryable: this.isRetryableError(error)
        });
      } finally {
        semaphore.release();
      }
    });
    
    await Promise.all(promises);
    return { successes, failures };
  }
  
  isRetryableError(error) {
    const retryableCodes = [
      'RATE_LIMIT_EXCEEDED',
      'INTERNAL_SERVER_ERROR',
      'SERVICE_UNAVAILABLE',
      'GATEWAY_TIMEOUT'
    ];
    return retryableCodes.includes(error.code);
  }
}

// Simple semaphore implementation
class Semaphore {
  constructor(limit) {
    this.limit = limit;
    this.current = 0;
    this.queue = [];
  }
  
  async acquire() {
    return new Promise((resolve) => {
      if (this.current < this.limit) {
        this.current++;
        resolve();
      } else {
        this.queue.push(resolve);
      }
    });
  }
  
  release() {
    this.current--;
    if (this.queue.length > 0) {
      const next = this.queue.shift();
      this.current++;
      next();
    }
  }
}

// Usage example
const batchProcessor = new BatchProcessor({
  batchSize: 50,
  maxConcurrency: 3
});

async function createCustomersBatch(customerDataList) {
  return await batchProcessor.processBatch(
    customerDataList,
    async (customerData) => {
      return await client.customers.create(customerData);
    }
  );
}
```

## Production Monitoring & Alerting

### Error Tracking

```javascript theme={null}
// ✅ Comprehensive error tracking and metrics
class ErrorTracker {
  constructor() {
    this.errorCounts = new Map();
    this.errorRates = new Map();
    this.alertThresholds = {
      errorRate: 0.05, // 5% error rate
      consecutiveErrors: 10,
      criticalErrors: ['UNAUTHORIZED', 'FORBIDDEN']
    };
  }
  
  trackError(error, context = {}) {
    const errorKey = `${error.code}_${context.operation || 'unknown'}`;
    
    // Update error counts
    this.errorCounts.set(errorKey, (this.errorCounts.get(errorKey) || 0) + 1);
    
    // Log error with context
    logger.error('StateSet API error tracked', {
      code: error.code,
      message: error.message,
      operation: context.operation,
      requestId: error.request_id,
      timestamp: new Date().toISOString(),
      ...context
    });
    
    // Check for critical errors
    if (this.alertThresholds.criticalErrors.includes(error.code)) {
      this.sendCriticalAlert(error, context);
    }
    
    // Check error rate thresholds
    this.checkErrorRateThreshold(errorKey, context);
  }
  
  trackSuccess(context = {}) {
    const operationKey = context.operation || 'unknown';
    this.updateSuccessRate(operationKey);
  }
  
  sendCriticalAlert(error, context) {
    // Integration with alerting system (PagerDuty, Slack, etc.)
    const alert = {
      severity: 'critical',
      title: `StateSet API Critical Error: ${error.code}`,
      description: error.message,
      context,
      timestamp: new Date().toISOString(),
      requestId: error.request_id
    };
    
    // Send alert to monitoring system
    this.sendAlert(alert);
  }
  
  async sendAlert(alert) {
    try {
      // Example: Send to Slack webhook
      await fetch(process.env.SLACK_WEBHOOK_URL, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          text: `🚨 ${alert.title}`,
          attachments: [{
            color: alert.severity === 'critical' ? 'danger' : 'warning',
            fields: [
              { title: 'Description', value: alert.description, short: false },
              { title: 'Request ID', value: alert.requestId, short: true },
              { title: 'Context', value: JSON.stringify(alert.context), short: true }
            ]
          }]
        })
      });
    } catch (alertError) {
      logger.error('Failed to send alert', { 
        originalAlert: alert,
        alertError: alertError.message 
      });
    }
  }
}

// Global error tracker instance
const errorTracker = new ErrorTracker();

// Enhanced API client with error tracking
class TrackedStateSetClient {
  constructor(options) {
    this.client = new StateSetClient(options);
    this.errorTracker = errorTracker;
  }
  
  async makeRequest(operation, requestFn) {
    try {
      const result = await requestFn();
      this.errorTracker.trackSuccess({ operation });
      return result;
    } catch (error) {
      this.errorTracker.trackError(error, { operation });
      throw error;
    }
  }
  
  // Wrap all client methods
  get customers() {
    return {
      create: (data) => this.makeRequest('customers.create', () => 
        this.client.customers.create(data)
      ),
      get: (id) => this.makeRequest('customers.get', () => 
        this.client.customers.get(id)
      ),
      list: (params) => this.makeRequest('customers.list', () => 
        this.client.customers.list(params)
      )
      // ... other methods
    };
  }
}
```

## Testing Error Scenarios

```javascript theme={null}
// ✅ Comprehensive error scenario testing
describe('StateSet API Error Handling', () => {
  let client;
  
  beforeEach(() => {
    client = new TrackedStateSetClient({
      apiKey: process.env.STATESET_TEST_API_KEY
    });
  });
  
  describe('Authentication Errors', () => {
    test('handles invalid API key gracefully', async () => {
      const invalidClient = new StateSetClient({ apiKey: 'invalid_key' });
      
      await expect(invalidClient.customers.list()).rejects.toMatchObject({
        code: 'UNAUTHORIZED',
        message: expect.stringContaining('Invalid API key')
      });
    });
    
    test('handles missing API key', async () => {
      expect(() => new StateSetClient({})).toThrow('API key is required');
    });
  });
  
  describe('Rate Limiting', () => {
    test('handles rate limit with retry', async () => {
      const rateLimitHandler = new RateLimitHandler({ maxRetries: 2 });
      
      // Mock rate limited responses
      const mockOperation = jest.fn()
        .mockRejectedValueOnce({ code: 'RATE_LIMIT_EXCEEDED', headers: { 'retry-after': '1' } })
        .mockResolvedValueOnce({ id: 'cust_123' });
      
      const result = await rateLimitHandler.executeWithRetry(mockOperation);
      
      expect(mockOperation).toHaveBeenCalledTimes(2);
      expect(result).toEqual({ id: 'cust_123' });
    });
  });
  
  describe('Circuit Breaker', () => {
    test('opens circuit after threshold failures', async () => {
      const circuitBreaker = new CircuitBreaker({ failureThreshold: 2, timeout: 1000 });
      const failingOperation = jest.fn().mockRejectedValue(new Error('Service error'));
      const fallback = jest.fn().mockResolvedValue({ cached: true });
      
      // Trigger circuit breaker
      await expect(circuitBreaker.execute(failingOperation)).rejects.toThrow();
      await expect(circuitBreaker.execute(failingOperation)).rejects.toThrow();
      
      // Circuit should now be open
      const result = await circuitBreaker.execute(failingOperation, fallback);
      expect(result).toEqual({ cached: true });
      expect(fallback).toHaveBeenCalled();
    });
  });
});
```

## Error Handling Checklist

<Card title="Production Readiness Checklist" icon="clipboard-check">
  **Before deploying to production, ensure you have:**

  ✅ **Proper error categorization** - Handle different error types appropriately\
  ✅ **Retry logic** - Implement exponential backoff for retryable errors\
  ✅ **Circuit breakers** - Prevent cascading failures in distributed systems\
  ✅ **Fallback mechanisms** - Graceful degradation when APIs are unavailable\
  ✅ **Comprehensive logging** - Log errors with sufficient context for debugging\
  ✅ **Monitoring & alerting** - Set up alerts for critical error conditions\
  ✅ **Error testing** - Test all error scenarios in your test suite\
  ✅ **Documentation** - Document error handling strategies for your team
</Card>

## Best Practices Summary

1. **Always use environment variables** for API keys and secrets
2. **Implement proper logging** instead of console.log statements
3. **Handle rate limiting** with intelligent retry mechanisms
4. **Use circuit breakers** for external API calls
5. **Track error metrics** and set up appropriate alerts
6. **Test error scenarios** comprehensively
7. **Provide meaningful fallbacks** when possible
8. **Never log sensitive information** in error messages

***

<Note>
  **Need Help?**

  If you encounter persistent errors or need assistance with error handling strategies, our support team is here to help. Contact us at [support@StateSet.com](mailto:support@StateSet.com) with your request ID for faster resolution.
</Note>
