REST API Error Handling
REST API Error Handling
Section titled “REST API Error Handling”Complete reference for HTTP status codes, error response formats, and troubleshooting common API errors.
Error Response Format
Section titled “Error Response Format”All API errors follow a consistent JSON format:
{ "error": "Error type", "message": "Human-readable description", "code": "ERROR_CODE", "details": { "field": "Additional context" }}Fields:
error- Error category (string)message- Human-readable error descriptioncode- Machine-readable error code (uppercase snake_case)details- Optional additional context (object)
HTTP Status Codes
Section titled “HTTP Status Codes”2xx Success
Section titled “2xx Success”200 OK
Section titled “200 OK”Request succeeded with data in response.
GET /api/users/user-123Authorization: Bearer <token>
HTTP/1.1 200 OKContent-Type: application/json
{ "id": "user-123", "email": "user@example.com", "profile": { "firstName": "John" }}201 Created
Section titled “201 Created”Resource created successfully.
POST /api/usersAuthorization: Bearer <token>Content-Type: application/json
HTTP/1.1 201 CreatedContent-Type: application/jsonLocation: /api/users/user-456
{ "success": true, "userId": "user-456", "email": "newuser@example.com"}204 No Content
Section titled “204 No Content”Request succeeded with no response body.
DELETE /api/users/user-123Authorization: Bearer <token>
HTTP/1.1 204 No Content4xx Client Errors
Section titled “4xx Client Errors”400 Bad Request
Section titled “400 Bad Request”Invalid request syntax or validation errors.
POST /api/usersAuthorization: Bearer <token>Content-Type: application/json
{ "email": "invalid-email"}
HTTP/1.1 400 Bad RequestContent-Type: application/json
{ "error": "Validation failed", "message": "Request validation failed", "code": "VALIDATION_ERROR", "details": { "email": "Invalid email format", "password": "Password is required" }}Common causes:
- Missing required fields
- Invalid field values
- Malformed JSON
- Type mismatches
401 Unauthorized
Section titled “401 Unauthorized”Missing or invalid authentication.
GET /api/users/user-123
HTTP/1.1 401 UnauthorizedContent-Type: application/json
{ "error": "Unauthorized", "message": "No authorization header provided", "code": "NO_AUTH_HEADER"}Common causes:
- Missing Authorization header
- Invalid JWT token
- Expired token
- Token signature verification failed
Variations:
Invalid Token:
{ "error": "Unauthorized", "message": "Invalid or expired token", "code": "INVALID_TOKEN"}Token Expired:
{ "error": "Unauthorized", "message": "Token has expired", "code": "TOKEN_EXPIRED"}403 Forbidden
Section titled “403 Forbidden”Authenticated but insufficient permissions.
POST /api/usersAuthorization: Bearer <token>Content-Type: application/json
HTTP/1.1 403 ForbiddenContent-Type: application/json
{ "error": "Forbidden", "message": "Missing required permission: users:create", "code": "INSUFFICIENT_PERMISSIONS", "requiredPermission": "users:create"}Common causes:
- Missing required permissions
- Policy violation (business rule)
- Resource access denied
Variations:
Policy Violation:
{ "error": "Forbidden", "message": "Policy violation: Users can only update their own profile", "code": "POLICY_VIOLATION"}404 Not Found
Section titled “404 Not Found”Resource does not exist.
GET /api/users/user-999Authorization: Bearer <token>
HTTP/1.1 404 Not FoundContent-Type: application/json
{ "error": "Not Found", "message": "User not found", "code": "NOT_FOUND", "resourceType": "User", "resourceId": "user-999"}Common causes:
- Invalid resource ID
- Resource deleted
- Incorrect URL path
409 Conflict
Section titled “409 Conflict”Request conflicts with current state.
POST /api/usersAuthorization: Bearer <token>Content-Type: application/json
{ "email": "existing@example.com", "password": "password123"}
HTTP/1.1 409 ConflictContent-Type: application/json
{ "error": "Conflict", "message": "User with this email already exists", "code": "RESOURCE_CONFLICT", "conflictingField": "email"}Common causes:
- Duplicate unique field (email, username)
- Concurrent modification
- Business rule violation
422 Unprocessable Entity
Section titled “422 Unprocessable Entity”Semantically incorrect request.
POST /api/usersAuthorization: Bearer <token>Content-Type: application/json
{ "email": "user@example.com", "password": "123", "profile": { "firstName": "" }}
HTTP/1.1 422 Unprocessable EntityContent-Type: application/json
{ "error": "Validation failed", "message": "Input validation failed", "code": "VALIDATION_ERROR", "validationErrors": [ { "field": "password", "message": "Password must be at least 8 characters" }, { "field": "profile.firstName", "message": "First name cannot be empty" } ]}Common causes:
- Business rule violations
- Complex validation errors
- Invalid state transitions
429 Too Many Requests
Section titled “429 Too Many Requests”Rate limit exceeded.
GET /api/usersAuthorization: Bearer <token>
HTTP/1.1 429 Too Many RequestsContent-Type: application/jsonX-RateLimit-Limit: 1000X-RateLimit-Remaining: 0X-RateLimit-Reset: 1700060400
{ "error": "Too Many Requests", "message": "Rate limit exceeded", "code": "RATE_LIMIT_EXCEEDED", "retryAfter": 300}Headers:
X-RateLimit-Limit- Total requests allowed per windowX-RateLimit-Remaining- Requests remaining in windowX-RateLimit-Reset- Timestamp when window resets
5xx Server Errors
Section titled “5xx Server Errors”500 Internal Server Error
Section titled “500 Internal Server Error”Unexpected server error.
GET /api/users/user-123Authorization: Bearer <token>
HTTP/1.1 500 Internal Server ErrorContent-Type: application/json
{ "error": "Internal Server Error", "message": "An unexpected error occurred", "code": "INTERNAL_ERROR", "correlationId": "abc-123-def-456"}Common causes:
- Unhandled exception
- Database connection failure
- Service unavailable
- Message bus error
Never includes:
- Stack traces
- Internal paths
- Database errors
- Sensitive information
502 Bad Gateway
Section titled “502 Bad Gateway”Upstream service error.
GET /api/users/user-123Authorization: Bearer <token>
HTTP/1.1 502 Bad GatewayContent-Type: application/json
{ "error": "Bad Gateway", "message": "Upstream service unavailable", "code": "SERVICE_UNAVAILABLE", "service": "auth-service"}Common causes:
- Service not responding
- Message bus timeout
- Service crashed
- Network issue
503 Service Unavailable
Section titled “503 Service Unavailable”Service temporarily unavailable.
GET /api/usersAuthorization: Bearer <token>
HTTP/1.1 503 Service UnavailableContent-Type: application/jsonRetry-After: 60
{ "error": "Service Unavailable", "message": "Service is temporarily unavailable", "code": "SERVICE_UNAVAILABLE", "retryAfter": 60}Common causes:
- Maintenance mode
- Overloaded service
- Database connection pool exhausted
- Circuit breaker open
504 Gateway Timeout
Section titled “504 Gateway Timeout”Upstream service timeout.
GET /api/usersAuthorization: Bearer <token>
HTTP/1.1 504 Gateway TimeoutContent-Type: application/json
{ "error": "Gateway Timeout", "message": "Request timed out", "code": "TIMEOUT", "timeoutMs": 30000}Common causes:
- Long-running query
- Service not responding
- Message bus timeout
- Network latency
Error Codes Reference
Section titled “Error Codes Reference”Authentication Errors
Section titled “Authentication Errors”| Code | Status | Description |
|---|---|---|
NO_AUTH_HEADER | 401 | Missing Authorization header |
INVALID_TOKEN | 401 | Token format invalid or verification failed |
TOKEN_EXPIRED | 401 | JWT token has expired |
INVALID_CREDENTIALS | 401 | Email/password authentication failed |
Authorization Errors
Section titled “Authorization Errors”| Code | Status | Description |
|---|---|---|
INSUFFICIENT_PERMISSIONS | 403 | User lacks required permissions |
POLICY_VIOLATION | 403 | Business rule policy check failed |
ACCESS_DENIED | 403 | Resource access not allowed |
Validation Errors
Section titled “Validation Errors”| Code | Status | Description |
|---|---|---|
VALIDATION_ERROR | 400/422 | Input validation failed |
MISSING_REQUIRED_FIELD | 400 | Required field not provided |
INVALID_FORMAT | 400 | Field format invalid |
INVALID_TYPE | 400 | Field type mismatch |
Resource Errors
Section titled “Resource Errors”| Code | Status | Description |
|---|---|---|
NOT_FOUND | 404 | Resource does not exist |
RESOURCE_CONFLICT | 409 | Resource already exists or conflicts |
RESOURCE_DELETED | 410 | Resource was deleted |
System Errors
Section titled “System Errors”| Code | Status | Description |
|---|---|---|
INTERNAL_ERROR | 500 | Unexpected server error |
SERVICE_UNAVAILABLE | 503 | Service temporarily unavailable |
TIMEOUT | 504 | Request timeout |
RATE_LIMIT_EXCEEDED | 429 | Too many requests |
Troubleshooting Guide
Section titled “Troubleshooting Guide”401 Unauthorized
Section titled “401 Unauthorized”Check:
- Is Authorization header present?
- Is token format correct (
Bearer <token>)? - Is token expired? (check
expclaim) - Is JWT_SECRET correct?
Fix:
# Get new tokencurl -X POST http://localhost:3003/api/auth/authenticate \ -H "Content-Type: application/json" \ -d '{"email":"user@example.com","password":"password"}'
# Use in requestcurl -X GET http://localhost:3003/api/users/user-123 \ -H "Authorization: Bearer <new-token>"403 Forbidden
Section titled “403 Forbidden”Check:
- Does user have required permissions?
- Check JWT token
permissionsclaim - Review contract
@Commandor@Querydecorator - Check for policy violations
Fix:
# Check user permissionscurl -X GET http://localhost:3003/api/users/user-123/permissions \ -H "Authorization: Bearer <token>"
# Grant required permissioncurl -X POST http://localhost:3003/api/users/user-123/permissions \ -H "Authorization: Bearer <admin-token>" \ -H "Content-Type: application/json" \ -d '{"permission":"users:create"}'404 Not Found
Section titled “404 Not Found”Check:
- Is resource ID correct?
- Does resource exist?
- Is URL path correct?
- Was resource deleted?
Fix:
# List available resourcescurl -X GET http://localhost:3003/api/users \ -H "Authorization: Bearer <token>"
# Check specific resourcecurl -X GET http://localhost:3003/api/users/user-123 \ -H "Authorization: Bearer <token>"409 Conflict
Section titled “409 Conflict”Check:
- Is email/username unique?
- Is there a concurrent modification?
- Review business rules
Fix:
# Use different emailcurl -X POST http://localhost:3003/api/users \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{"email":"unique@example.com","password":"password123"}'422 Validation Error
Section titled “422 Validation Error”Check:
- Review validation errors in response
- Check required fields
- Verify field formats
- Review business rules
Fix:
# Correct validation errorscurl -X POST http://localhost:3003/api/users \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "email":"valid@example.com", "password":"SecurePassword123!", "profile":{"firstName":"John","lastName":"Doe"} }'500 Internal Server Error
Section titled “500 Internal Server Error”Check:
- Review server logs
- Check
correlationIdfor tracing - Verify service health
- Check database connectivity
Fix:
# Check service healthcurl -X GET http://localhost:3003/health
# Check logsdocker logs api-gatewaydocker logs auth-service
# Restart services if neededdocker compose restart503 Service Unavailable
Section titled “503 Service Unavailable”Check:
- Is service running?
- Check service health
- Review resource usage
- Check message bus connectivity
Fix:
# Check service statusdocker compose ps
# Check service healthcurl -X GET http://localhost:3003/health
# Restart servicesdocker compose restart auth-serviceBest Practices
Section titled “Best Practices”Error Handling in Clients
Section titled “Error Handling in Clients”async function handleApiRequest(url: string, options: RequestInit) { try { const response = await fetch(url, options);
// Success if (response.ok) { return await response.json(); }
// Parse error const error = await response.json();
// Handle specific errors switch (response.status) { case 401: // Token expired, try refresh await refreshToken(); return handleApiRequest(url, options); // Retry
case 403: // Insufficient permissions showPermissionError(error.message); break;
case 404: // Resource not found showNotFoundError(error.message); break;
case 422: // Validation errors showValidationErrors(error.validationErrors); break;
case 429: // Rate limited const retryAfter = error.retryAfter || 60; await sleep(retryAfter * 1000); return handleApiRequest(url, options); // Retry
case 500: case 502: case 503: // Server errors - retry with backoff await exponentialBackoff(); return handleApiRequest(url, options); // Retry
default: showGenericError(error.message); }
throw new Error(error.message); } catch (err) { // Network error or JSON parse error console.error('Request failed:', err); throw err; }}Logging Errors
Section titled “Logging Errors”function logApiError(error: any, context: Record<string, any>) { console.error('API Error:', { status: error.status, code: error.code, message: error.message, correlationId: error.correlationId, ...context });}Retry Logic
Section titled “Retry Logic”async function retryWithBackoff( fn: () => Promise<any>, maxRetries: number = 3) { for (let i = 0; i < maxRetries; i++) { try { return await fn(); } catch (error: any) { // Don't retry client errors (4xx) if (error.status >= 400 && error.status < 500) { throw error; }
// Retry server errors (5xx) if (i === maxRetries - 1) { throw error; }
const delay = Math.min(1000 * 2 ** i, 10000); await sleep(delay); } }}Next Steps
Section titled “Next Steps”- REST API Authentication - Authentication methods
- REST API Endpoints - Complete endpoint reference
- REST API Overview - REST API concepts
- GraphQL API - Alternative API option