Appearance
Error Handling Guide
Table of Contents
- Overview
- Error Response Format
- HTTP Status Codes
- Error Categories
- Common Error Scenarios
- Resolution Strategies
- Retry Logic
- Debugging Tips
- 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
| Field | Type | Description |
|---|---|---|
code | string | Machine-readable error code |
message | string | Human-readable error message |
details | object | Additional context or field-specific errors |
timestamp | datetime | When the error occurred (ISO 8601) |
traceId | string | Unique identifier for request tracing |
path | string | API endpoint that generated the error |
method | string | HTTP method used |
HTTP Status Codes
Success Codes (2xx)
| Status | Code | Description | Usage |
|---|---|---|---|
| 200 | OK | Request successful | GET, PUT, DELETE success |
| 201 | Created | Resource created | POST success with new resource |
| 204 | No Content | Success, no response body | DELETE with no return data |
Client Error Codes (4xx)
| Status | Code | Description | Common Causes |
|---|---|---|---|
| 400 | Bad Request | Invalid request format | Malformed JSON, missing fields |
| 401 | Unauthorized | Authentication failed | Invalid/expired token |
| 403 | Forbidden | Access denied | Insufficient permissions |
| 404 | Not Found | Resource not found | Invalid ID, deleted resource |
| 409 | Conflict | Resource conflict | Duplicate names, state conflicts |
| 422 | Unprocessable Entity | Validation failed | Business rule violations |
| 429 | Too Many Requests | Rate limit exceeded | Too many API calls |
Server Error Codes (5xx)
| Status | Code | Description | Response Strategy |
|---|---|---|---|
| 500 | Internal Server Error | Server error | Retry with backoff |
| 502 | Bad Gateway | Gateway error | Retry with backoff |
| 503 | Service Unavailable | Service down | Check status page |
| 504 | Gateway Timeout | Request timeout | Retry 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 tokenAUTH_TOKEN_EXPIRED: Token past expirationAUTH_TOKEN_MISSING: No authorization headerAUTH_INSUFFICIENT_SCOPE: Token lacks required scopeAUTH_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 fieldVALIDATION_INVALID_FORMAT: Wrong data formatVALIDATION_OUT_OF_RANGE: Value outside allowed rangeVALIDATION_DUPLICATE: Duplicate value where unique requiredVALIDATION_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 spotsBUSINESS_REGISTRATION_CLOSED: Registration period endedBUSINESS_INSUFFICIENT_FUNDS: Payment amount insufficientBUSINESS_APPROVAL_REQUIRED: Needs approval before proceedingBUSINESS_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 existRESOURCE_DELETED: Entity has been deletedRESOURCE_LOCKED: Entity is locked for editingRESOURCE_CONFLICT: Conflicting state or duplicateRESOURCE_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 permissionPERMISSION_TENANT_MISMATCH: Wrong tenant contextPERMISSION_OWNER_ONLY: Only owner can perform actionPERMISSION_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: 587Common 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:
- Check event capacity and waitlist options
- Verify registration dates
- Confirm member eligibility requirements
- 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:
- Query available booths
- Submit alternative booth preferences
- Contact event coordinator for manual assignment
- 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:
- Process successful items
- Fix and retry failed items
- 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 request3. 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 Type | Retry? | Strategy | Max Attempts |
|---|---|---|---|
| 401 Unauthorized | Yes | Refresh token first | 1 |
| 429 Rate Limited | Yes | Use Retry-After header | 3 |
| 500 Server Error | Yes | Exponential backoff | 3 |
| 502 Bad Gateway | Yes | Exponential backoff | 3 |
| 503 Service Unavailable | Yes | Exponential backoff | 5 |
| 504 Gateway Timeout | Yes | Reduce payload, retry | 2 |
| 400 Bad Request | No | Fix request | 0 |
| 403 Forbidden | No | Check permissions | 0 |
| 404 Not Found | No | Verify resource | 0 |
| 409 Conflict | Maybe | Check if resolved | 1 |
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:
Error Details
- Error code and message
- HTTP status code
- Trace ID
- Timestamp
Request Context
- API endpoint
- HTTP method
- Request headers (excluding auth)
- Request body (sanitized)
Environment
- API version
- Client library version
- Programming language/platform
- Network environment
Support Channels
- Email: api-support@engagifii.com
- Developer Portal: https://developers.engagifii.com
- Status Page: https://status.engagifii.com
- Community Forum: https://community.engagifii.com/api
