Error Handling
Understanding API errors and how to handle them gracefully.
Error Response Format
All error responses follow a consistent JSON format:
{
"error": "Human-readable error message",
"code": "ERROR_CODE",
"errors": ["Detailed error 1", "Detailed error 2"]
}error- A human-readable description of the errorcode- A machine-readable error code (optional)errors- Array of detailed error messages (for validation errors)
HTTP Status Codes
Status Codes
| Parameter | Type | Description |
|---|---|---|
400 | BAD_REQUEST | Invalid request body or parameters |
401 | UNAUTHORIZED | Missing or invalid API key |
403 | FORBIDDEN | Insufficient permissions (scope) |
404 | NOT_FOUND | Resource not found |
409 | CONFLICT | Resource conflict (e.g., duplicate, slot taken) |
429 | RATE_LIMITED | Too many requests |
500 | INTERNAL_ERROR | Server error |
Common Error Examples
Authentication Error
401 Unauthorized
{
"error": "API key is required",
"code": "UNAUTHORIZED"
}Permission Error
403 Forbidden
{
"error": "Insufficient permissions. Required scope: events:write",
"code": "FORBIDDEN"
}Validation Error
400 Bad Request
{
"error": "Validation failed",
"errors": [
"title is required",
"start_date must be a valid ISO 8601 date",
"slots must be a positive number"
]
}Resource Not Found
404 Not Found
{
"error": "Event not found",
"code": "NOT_FOUND"
}Booking Conflict
409 Conflict
{
"error": "This time slot is already booked",
"code": "SLOT_TAKEN"
}Rate Limited
429 Too Many Requests
{
"error": "Rate limit exceeded",
"code": "RATE_LIMITED",
"retry_after": 45
}Handling Errors in Code
error-handling.ts
async function createEvent(eventData: EventInput) {
try {
const response = await fetch('/api/v1/events', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(eventData),
});
if (!response.ok) {
const error = await response.json();
switch (response.status) {
case 400:
// Validation error - show to user
console.error('Validation failed:', error.errors);
throw new ValidationError(error.errors);
case 401:
// Auth error - redirect to login
throw new AuthError('Please log in again');
case 403:
// Permission error - show message
throw new PermissionError(error.error);
case 429:
// Rate limited - retry later
const retryAfter = error.retry_after || 60;
throw new RateLimitError(`Try again in ${retryAfter}s`);
default:
throw new ApiError(error.error || 'An error occurred');
}
}
return await response.json();
} catch (error) {
// Handle network errors
if (error instanceof TypeError) {
throw new NetworkError('Please check your connection');
}
throw error;
}
}Best Practices
Always Check Status Codes
Don't assume a response is successful. Always check the HTTP status code before processing the response body.
Handle Validation Errors Gracefully
When you receive a 400 error with an errors array, display these messages to your users so they can correct their input.
Implement Retry Logic
For 429 and 5xx errors, implement exponential backoff retry logic. Use the Retry-After header when available.
Log Errors for Debugging
Log the full error response including status code, error message, and any error codes for easier debugging.