Skip to content

Error Handling Guide

Table of Contents

  1. Overview
  2. Error Response Format
  3. HTTP Status Codes
  4. Error Categories
  5. Common Error Scenarios
  6. Resolution Strategies
  7. Retry Logic
  8. Debugging Tips
  9. Error Handling Examples

Overview

The Engagifii Events API uses standardized error responses to help developers quickly identify and resolve issues. All errors follow a consistent format and include actionable information for troubleshooting.

Error Response Format

Standard Error Structure

All API errors return a consistent JSON structure:

json
{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error description",
    "details": {
      "field1": ["Validation error 1", "Validation error 2"],
      "field2": ["Validation error 3"]
    },
    "timestamp": "2024-01-15T10:30:45.123Z",
    "traceId": "abc123def456",
    "path": "/api/1.0/event/save",
    "method": "POST"
  }
}

Error Response Fields

FieldTypeDescription
codestringMachine-readable error code
messagestringHuman-readable error message
detailsobjectAdditional context or field-specific errors
timestampdatetimeWhen the error occurred (ISO 8601)
traceIdstringUnique identifier for request tracing
pathstringAPI endpoint that generated the error
methodstringHTTP method used

HTTP Status Codes

Success Codes (2xx)

StatusCodeDescriptionUsage
200OKRequest successfulGET, PUT, DELETE success
201CreatedResource createdPOST success with new resource
204No ContentSuccess, no response bodyDELETE with no return data

Client Error Codes (4xx)

StatusCodeDescriptionCommon Causes
400Bad RequestInvalid request formatMalformed JSON, missing fields
401UnauthorizedAuthentication failedInvalid/expired token
403ForbiddenAccess deniedInsufficient permissions
404Not FoundResource not foundInvalid ID, deleted resource
409ConflictResource conflictDuplicate names, state conflicts
422Unprocessable EntityValidation failedBusiness rule violations
429Too Many RequestsRate limit exceededToo many API calls

Server Error Codes (5xx)

StatusCodeDescriptionResponse Strategy
500Internal Server ErrorServer errorRetry with backoff
502Bad GatewayGateway errorRetry with backoff
503Service UnavailableService downCheck status page
504Gateway TimeoutRequest timeoutRetry with smaller payload

Error Categories

1. Authentication Errors

Code Pattern: AUTH_*

json
{
  "error": {
    "code": "AUTH_TOKEN_EXPIRED",
    "message": "Access token has expired",
    "details": {
      "expiredAt": "2024-01-15T09:30:00Z",
      "hint": "Request new token using refresh token or client credentials"
    }
  }
}

Common Codes:

  • AUTH_TOKEN_INVALID: Malformed or invalid token
  • AUTH_TOKEN_EXPIRED: Token past expiration
  • AUTH_TOKEN_MISSING: No authorization header
  • AUTH_INSUFFICIENT_SCOPE: Token lacks required scope
  • AUTH_CLIENT_INVALID: Invalid client credentials

2. Validation Errors

Code Pattern: VALIDATION_*

json
{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "Request validation failed",
    "details": {
      "name": ["Name is required", "Name must be less than 500 characters"],
      "startDateTime": ["Start date must be in the future"],
      "capacity": ["Capacity must be greater than 0"]
    }
  }
}

Common Codes:

  • VALIDATION_REQUIRED_FIELD: Missing required field
  • VALIDATION_INVALID_FORMAT: Wrong data format
  • VALIDATION_OUT_OF_RANGE: Value outside allowed range
  • VALIDATION_DUPLICATE: Duplicate value where unique required
  • VALIDATION_REFERENCE_INVALID: Referenced entity doesn't exist

3. Business Logic Errors

Code Pattern: BUSINESS_*

json
{
  "error": {
    "code": "BUSINESS_EVENT_FULL",
    "message": "Event has reached maximum capacity",
    "details": {
      "eventId": "event-guid",
      "capacity": 500,
      "registered": 500,
      "waitlistAvailable": true
    }
  }
}

Common Codes:

  • BUSINESS_EVENT_FULL: No available spots
  • BUSINESS_REGISTRATION_CLOSED: Registration period ended
  • BUSINESS_INSUFFICIENT_FUNDS: Payment amount insufficient
  • BUSINESS_APPROVAL_REQUIRED: Needs approval before proceeding
  • BUSINESS_PREREQUISITE_NOT_MET: Missing required prerequisites

4. Resource Errors

Code Pattern: RESOURCE_*

json
{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Event not found",
    "details": {
      "resourceType": "Event",
      "resourceId": "nonexistent-guid"
    }
  }
}

Common Codes:

  • RESOURCE_NOT_FOUND: Entity doesn't exist
  • RESOURCE_DELETED: Entity has been deleted
  • RESOURCE_LOCKED: Entity is locked for editing
  • RESOURCE_CONFLICT: Conflicting state or duplicate
  • RESOURCE_DEPENDENCY: Cannot delete due to dependencies

5. Permission Errors

Code Pattern: PERMISSION_*

json
{
  "error": {
    "code": "PERMISSION_DENIED",
    "message": "You do not have permission to edit this event",
    "details": {
      "requiredRole": "EventManager",
      "userRoles": ["EventViewer"],
      "action": "UpdateEvent"
    }
  }
}

Common Codes:

  • PERMISSION_DENIED: Lacking required permission
  • PERMISSION_TENANT_MISMATCH: Wrong tenant context
  • PERMISSION_OWNER_ONLY: Only owner can perform action
  • PERMISSION_ROLE_REQUIRED: Specific role needed

6. Rate Limiting Errors

Code Pattern: RATE_*

json
{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "API rate limit exceeded",
    "details": {
      "limit": 1000,
      "remaining": 0,
      "resetAt": "2024-01-15T11:00:00Z",
      "retryAfter": 587
    }
  }
}

Headers included:

http
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705316400
Retry-After: 587

Common Error Scenarios

Scenario 1: Event Registration Fails

json
{
  "error": {
    "code": "BUSINESS_REGISTRATION_BLOCKED",
    "message": "Registration cannot be completed",
    "details": {
      "reasons": [
        "Event capacity reached",
        "Registration deadline passed",
        "Member not eligible"
      ],
      "eventId": "event-guid",
      "memberId": "member-guid"
    }
  }
}

Resolution Steps:

  1. Check event capacity and waitlist options
  2. Verify registration dates
  3. Confirm member eligibility requirements
  4. Consider alternative events or sessions

Scenario 2: Booth Assignment Conflict

json
{
  "error": {
    "code": "RESOURCE_CONFLICT",
    "message": "Booth already assigned",
    "details": {
      "boothId": "booth-guid",
      "boothName": "A101",
      "assignedTo": "Other Company Inc.",
      "assignedDate": "2024-01-10T14:30:00Z"
    }
  }
}

Resolution Steps:

  1. Query available booths
  2. Submit alternative booth preferences
  3. Contact event coordinator for manual assignment
  4. Consider booth sharing options

Scenario 3: Bulk Operation Partial Failure

json
{
  "error": {
    "code": "PARTIAL_SUCCESS",
    "message": "Some operations failed",
    "details": {
      "succeeded": 8,
      "failed": 2,
      "failures": [
        {
          "index": 3,
          "id": "item-3-guid",
          "error": "Duplicate name"
        },
        {
          "index": 7,
          "id": "item-7-guid",
          "error": "Invalid date"
        }
      ]
    }
  }
}

Resolution Steps:

  1. Process successful items
  2. Fix and retry failed items
  3. Consider transactional approach if atomicity required

Resolution Strategies

1. Automatic Retry Strategy

javascript
class ApiClient {
  async retryableRequest(fn, maxRetries = 3) {
    const retryableErrors = [429, 502, 503, 504];
    let lastError;
    
    for (let i = 0; i <= maxRetries; i++) {
      try {
        return await fn();
      } catch (error) {
        lastError = error;
        
        if (!retryableErrors.includes(error.status) || i === maxRetries) {
          throw error;
        }
        
        const delay = this.calculateBackoff(i, error);
        await this.sleep(delay);
      }
    }
    
    throw lastError;
  }
  
  calculateBackoff(attempt, error) {
    // Exponential backoff with jitter
    const baseDelay = 1000;
    const maxDelay = 30000;
    
    // Use Retry-After header if available
    if (error.headers?.['retry-after']) {
      return parseInt(error.headers['retry-after']) * 1000;
    }
    
    const exponentialDelay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
    const jitter = Math.random() * 1000;
    
    return exponentialDelay + jitter;
  }
  
  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

2. Error Recovery Patterns

python
class ErrorRecovery:
    def handle_api_error(self, error):
        """Intelligent error recovery based on error type"""
        
        recovery_strategies = {
            'AUTH_TOKEN_EXPIRED': self.refresh_token,
            'RESOURCE_NOT_FOUND': self.handle_not_found,
            'VALIDATION_FAILED': self.handle_validation,
            'RATE_LIMIT_EXCEEDED': self.handle_rate_limit,
            'BUSINESS_EVENT_FULL': self.handle_capacity
        }
        
        strategy = recovery_strategies.get(error.code)
        if strategy:
            return strategy(error)
        
        # Default handling
        self.log_error(error)
        raise error
    
    async def refresh_token(self, error):
        """Get new token and retry request"""
        new_token = await self.auth_client.get_new_token()
        self.client.set_token(new_token)
        return True  # Retry request
    
    def handle_validation(self, error):
        """Process validation errors"""
        for field, errors in error.details.items():
            print(f"Field '{field}': {', '.join(errors)}")
        return False  # Don't retry
    
    def handle_rate_limit(self, error):
        """Wait and retry after rate limit"""
        retry_after = error.details.get('retryAfter', 60)
        print(f"Rate limited. Waiting {retry_after} seconds...")
        time.sleep(retry_after)
        return True  # Retry request

3. Circuit Breaker Pattern

csharp
public class CircuitBreaker
{
    private int failureCount = 0;
    private DateTime lastFailureTime;
    private readonly int threshold = 5;
    private readonly TimeSpan timeout = TimeSpan.FromMinutes(1);
    
    public enum State { Closed, Open, HalfOpen }
    public State CurrentState { get; private set; } = State.Closed;
    
    public async Task<T> ExecuteAsync<T>(Func<Task<T>> action)
    {
        if (CurrentState == State.Open)
        {
            if (DateTime.UtcNow - lastFailureTime > timeout)
            {
                CurrentState = State.HalfOpen;
            }
            else
            {
                throw new CircuitBreakerOpenException("Circuit breaker is open");
            }
        }
        
        try
        {
            var result = await action();
            OnSuccess();
            return result;
        }
        catch (Exception ex)
        {
            OnFailure();
            throw;
        }
    }
    
    private void OnSuccess()
    {
        failureCount = 0;
        CurrentState = State.Closed;
    }
    
    private void OnFailure()
    {
        lastFailureTime = DateTime.UtcNow;
        failureCount++;
        
        if (failureCount >= threshold)
        {
            CurrentState = State.Open;
        }
    }
}

Retry Logic

Retry Decision Matrix

Error TypeRetry?StrategyMax Attempts
401 UnauthorizedYesRefresh token first1
429 Rate LimitedYesUse Retry-After header3
500 Server ErrorYesExponential backoff3
502 Bad GatewayYesExponential backoff3
503 Service UnavailableYesExponential backoff5
504 Gateway TimeoutYesReduce payload, retry2
400 Bad RequestNoFix request0
403 ForbiddenNoCheck permissions0
404 Not FoundNoVerify resource0
409 ConflictMaybeCheck if resolved1

Implementing Smart Retry

javascript
class SmartRetryClient {
  constructor() {
    this.retryConfig = {
      maxAttempts: 3,
      baseDelay: 1000,
      maxDelay: 30000,
      retryableStatuses: [429, 500, 502, 503, 504],
      retryableErrors: ['ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND']
    };
  }
  
  async request(options, attempt = 1) {
    try {
      const response = await this.httpClient(options);
      return this.handleSuccess(response);
    } catch (error) {
      return this.handleError(error, options, attempt);
    }
  }
  
  async handleError(error, options, attempt) {
    // Log error details
    console.error(`Attempt ${attempt} failed:`, {
      status: error.status,
      code: error.code,
      message: error.message,
      traceId: error.traceId
    });
    
    // Check if retryable
    if (!this.shouldRetry(error, attempt)) {
      throw this.enhanceError(error);
    }
    
    // Calculate delay
    const delay = this.getRetryDelay(error, attempt);
    console.log(`Retrying after ${delay}ms...`);
    
    // Wait and retry
    await this.sleep(delay);
    return this.request(options, attempt + 1);
  }
  
  shouldRetry(error, attempt) {
    if (attempt >= this.retryConfig.maxAttempts) {
      return false;
    }
    
    // Check HTTP status
    if (error.status && this.retryConfig.retryableStatuses.includes(error.status)) {
      return true;
    }
    
    // Check network errors
    if (error.code && this.retryConfig.retryableErrors.includes(error.code)) {
      return true;
    }
    
    // Special handling for 401
    if (error.status === 401 && attempt === 1) {
      // Try refreshing token once
      return true;
    }
    
    return false;
  }
  
  getRetryDelay(error, attempt) {
    // Use server-provided retry delay
    if (error.headers?.['retry-after']) {
      const retryAfter = error.headers['retry-after'];
      return parseInt(retryAfter) * 1000;
    }
    
    // Exponential backoff with jitter
    const baseDelay = this.retryConfig.baseDelay;
    const exponential = Math.min(
      baseDelay * Math.pow(2, attempt - 1),
      this.retryConfig.maxDelay
    );
    const jitter = Math.random() * 1000;
    
    return exponential + jitter;
  }
  
  enhanceError(error) {
    // Add helpful context
    error.retryable = this.retryConfig.retryableStatuses.includes(error.status);
    error.temporary = error.status >= 500;
    
    // Add resolution hints
    if (error.code === 'AUTH_TOKEN_EXPIRED') {
      error.resolution = 'Refresh authentication token';
    } else if (error.code === 'VALIDATION_FAILED') {
      error.resolution = 'Check request payload against API documentation';
    } else if (error.status === 404) {
      error.resolution = 'Verify resource ID and endpoint path';
    }
    
    return error;
  }
}

Debugging Tips

1. Enable Debug Logging

javascript
// Set debug environment variable
process.env.DEBUG = 'engagifii:*';

// Or use detailed logging
const client = new EngagifiiClient({
  debug: true,
  logLevel: 'debug',
  logger: {
    request: (config) => console.log('Request:', config),
    response: (response) => console.log('Response:', response),
    error: (error) => console.error('Error:', error)
  }
});

2. Capture Request/Response Details

python
import logging
from datetime import datetime

class DebugInterceptor:
    def __init__(self):
        self.logger = logging.getLogger('engagifii.debug')
        
    def log_request(self, request):
        self.logger.debug(f"""
        ========== REQUEST ==========
        Time: {datetime.now().isoformat()}
        Method: {request.method}
        URL: {request.url}
        Headers: {request.headers}
        Body: {request.json if hasattr(request, 'json') else 'N/A'}
        =============================
        """)
    
    def log_response(self, response):
        self.logger.debug(f"""
        ========== RESPONSE ==========
        Time: {datetime.now().isoformat()}
        Status: {response.status_code}
        Headers: {response.headers}
        Body: {response.text[:1000]}  # First 1000 chars
        Trace ID: {response.headers.get('X-Trace-Id')}
        ==============================
        """)
    
    def log_error(self, error):
        self.logger.error(f"""
        ========== ERROR ==========
        Time: {datetime.now().isoformat()}
        Type: {type(error).__name__}
        Message: {str(error)}
        Code: {getattr(error, 'code', 'N/A')}
        Trace ID: {getattr(error, 'trace_id', 'N/A')}
        Details: {getattr(error, 'details', 'N/A')}
        ===========================
        """)

3. Use Trace IDs

Always include trace IDs in error reports:

csharp
public class ErrorReport
{
    public string TraceId { get; set; }
    public string ErrorCode { get; set; }
    public DateTime Timestamp { get; set; }
    public string Endpoint { get; set; }
    public object RequestPayload { get; set; }
    public object ErrorDetails { get; set; }
    
    public void SendToSupport()
    {
        var report = JsonConvert.SerializeObject(this, Formatting.Indented);
        
        // Include in support ticket
        var ticket = new SupportTicket
        {
            Subject = $"API Error: {ErrorCode}",
            Body = $"Trace ID: {TraceId}\n\nDetails:\n{report}",
            Priority = GetPriority(ErrorCode)
        };
        
        SupportSystem.CreateTicket(ticket);
    }
}

4. Common Debugging Checklist

  • [ ] Verify authentication token is valid and not expired
  • [ ] Check all required headers are present (api-version, tenant-code)
  • [ ] Validate request payload matches API specification
  • [ ] Confirm resource IDs exist and are accessible
  • [ ] Verify user has required permissions
  • [ ] Check for rate limiting (review X-RateLimit headers)
  • [ ] Test with minimal payload to isolate issues
  • [ ] Review server status page for outages
  • [ ] Check network connectivity and firewall rules
  • [ ] Validate date/time formats and timezones

Error Handling Examples

Complete Error Handler (JavaScript)

javascript
class ComprehensiveErrorHandler {
  handle(error) {
    // Parse error response
    const errorData = this.parseError(error);
    
    // Log for debugging
    this.logError(errorData);
    
    // Determine action
    const action = this.determineAction(errorData);
    
    // Execute recovery
    switch (action) {
      case 'RETRY':
        return { retry: true, delay: this.getRetryDelay(errorData) };
      
      case 'REFRESH_TOKEN':
        return { refreshToken: true };
      
      case 'USER_ACTION':
        return { 
          userMessage: this.getUserMessage(errorData),
          suggestions: this.getSuggestions(errorData)
        };
      
      case 'FATAL':
        throw new FatalApiError(errorData);
      
      default:
        throw error;
    }
  }
  
  parseError(error) {
    if (error.response) {
      // API error response
      return {
        status: error.response.status,
        code: error.response.data?.error?.code,
        message: error.response.data?.error?.message,
        details: error.response.data?.error?.details,
        traceId: error.response.data?.error?.traceId,
        headers: error.response.headers
      };
    } else if (error.request) {
      // No response received
      return {
        code: 'NETWORK_ERROR',
        message: 'No response from server',
        details: { originalError: error.message }
      };
    } else {
      // Request setup error
      return {
        code: 'REQUEST_ERROR',
        message: 'Error setting up request',
        details: { originalError: error.message }
      };
    }
  }
  
  determineAction(errorData) {
    const { status, code } = errorData;
    
    // Network errors - retry
    if (code === 'NETWORK_ERROR') return 'RETRY';
    
    // Auth errors
    if (status === 401) return 'REFRESH_TOKEN';
    
    // Server errors - retry
    if (status >= 500) return 'RETRY';
    
    // Rate limiting - retry with delay
    if (status === 429) return 'RETRY';
    
    // Client errors - user action needed
    if (status >= 400 && status < 500) return 'USER_ACTION';
    
    // Unknown - fatal
    return 'FATAL';
  }
  
  getUserMessage(errorData) {
    const messages = {
      'VALIDATION_FAILED': 'Please check your input and try again',
      'RESOURCE_NOT_FOUND': 'The requested item was not found',
      'PERMISSION_DENIED': 'You don\'t have permission for this action',
      'BUSINESS_EVENT_FULL': 'This event is at full capacity',
      'RATE_LIMIT_EXCEEDED': 'Too many requests. Please wait a moment'
    };
    
    return messages[errorData.code] || errorData.message || 'An error occurred';
  }
  
  getSuggestions(errorData) {
    const suggestions = {
      'VALIDATION_FAILED': [
        'Review the field requirements',
        'Check for required fields',
        'Verify data formats'
      ],
      'RESOURCE_NOT_FOUND': [
        'Verify the ID is correct',
        'Check if the resource was deleted',
        'Ensure you have access to this resource'
      ],
      'AUTH_TOKEN_EXPIRED': [
        'Sign in again',
        'Refresh your session'
      ]
    };
    
    return suggestions[errorData.code] || ['Please try again or contact support'];
  }
}

Support Resources

Getting Help

When contacting support, provide:

  1. Error Details

    • Error code and message
    • HTTP status code
    • Trace ID
    • Timestamp
  2. Request Context

    • API endpoint
    • HTTP method
    • Request headers (excluding auth)
    • Request body (sanitized)
  3. Environment

    • API version
    • Client library version
    • Programming language/platform
    • Network environment

Support Channels