Error Response Format
All JobHive API errors follow a consistent JSON structure to help you handle them programmatically:{
"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:2xx Success Codes
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 |
4xx Client Error Codes
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 |
5xx Server Error Codes
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 |
Common Error Types
Authentication Errors (401)
- Missing API Key
- Invalid API Key
- Expired API Key
{
"success": false,
"error": {
"code": "AUTHENTICATION_REQUIRED",
"message": "API key is required. Include 'Authorization: Bearer YOUR_API_KEY' header.",
"details": {
"header_missing": "Authorization"
}
}
}
headers: {
'Authorization': `Bearer ${process.env.JOBHIVE_API_KEY}`,
'Content-Type': 'application/json'
}
{
"success": false,
"error": {
"code": "INVALID_API_KEY",
"message": "The provided API key is invalid or has been revoked.",
"details": {
"key_prefix": "jh_live_abc123"
}
}
}
{
"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"
}
}
}
Validation Errors (400/422)
- Missing Required Fields
- Invalid Field Values
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Required fields are missing",
"details": {
"missing_fields": ["candidate_email", "position"],
"provided_fields": ["skills", "duration_minutes"]
}
}
}
{
"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"
}
}
}
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);
}
}
Rate Limiting (429)
{
"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
}
}
}
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640995260
X-RateLimit-Retry-After: 60
Resource Errors (404/409)
- Interview Not Found
- Invalid State Transition
{
"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"
}
}
}
{
"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"]
}
}
}
Error Handling Patterns
Basic Error Handling
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);
}
}
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}")
Retry Logic with Exponential Backoff
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);
}
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}")
Circuit Breaker Pattern
Implement a circuit breaker to handle sustained failures gracefully: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);
}
}
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}")
Error Recovery Strategies
Graceful Degradation
Fallback to Manual Process
Fallback to Manual Process
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'
};
}
}
Queue for Later Processing
Queue for Later Processing
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')
Error Monitoring and Alerting
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
}
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))
Best Practices Summary
Proactive Error Handling
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
User Experience
UX Considerations
- Provide clear error messages to users
- Implement graceful degradation
- Show loading states during retries
- Offer alternative actions when possible
Monitoring & Alerting
Observability Setup
- Log all errors with context
- Set up alerts for critical errors
- Track error trends over time
- Generate regular error reports
Recovery Strategies
Resilience Patterns
- Queue failed requests for retry
- Implement manual fallback processes
- Use multiple API keys for redundancy
- Plan for maintenance windows
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.
Support Resources: For persistent issues, contact our support team at dev-exec@jobhive.ai with your request IDs and error logs for faster resolution.
