API Error Handling
Design clear, consistent, and helpful error responses that make debugging easy and improve developer experience
Standard Error Response Format
A well-designed error response should be consistent, informative, and actionable. Every error response from your API should follow the same structure, making it easy for clients to parse and handle errors programmatically.
- Consistent structure across all endpoints
- Machine-readable error codes
- Human-readable messages
- Detailed field-level errors when applicable
- Request tracking for debugging
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": [
{
"field": "email",
"message": "Invalid email format"
},
{
"field": "age",
"message": "Must be a positive number"
}
],
"request_id": "req_abc123xyz"
}
}
Error Codes vs HTTP Status
HTTP status codes and application error codes serve different purposes. Understanding when to use each is crucial for a well-designed API. See our complete status codes reference for all HTTP codes.
🌐 HTTP Status Codes
Transport-level indicators. They tell the client about the general category of the response (success, client error, server error). Proxies, load balancers, and HTTP clients use these.
🏷️ Application Error Codes
Business logic identifiers. They provide specific, machine-readable codes that clients can use to handle errors programmatically and show appropriate UI messages.
Error Code Mapping
Validation Errors
Return all validation errors at once. Don't make users fix one error at a time.
Field-Level Errors
Always include the field name and a clear message for each invalid field.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Must be a valid email"
}
]
}
}
Multiple Errors
Return all errors in a single response so clients can fix everything at once.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Multiple validation errors",
"details": [
{"field": "email", "message": "Required"},
{"field": "password", "message": "Min 8 chars"},
{"field": "age", "message": "Must be 18+"}
]
}
}
Nested Field Paths
Use dot notation or JSON pointer for nested objects and arrays.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid address data",
"details": [
{
"field": "address.zip_code",
"message": "Invalid format"
},
{
"field": "items[0].quantity",
"message": "Must be positive"
}
]
}
}
Common Error Scenarios
400 Bad Request - Invalid Input
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": [
{"field": "email", "message": "Invalid email format"},
{"field": "password", "message": "Must be at least 8 characters"}
],
"request_id": "req_2xK9mN3p"
}
}
401 Unauthorized - Authentication Required
{
"error": {
"code": "AUTH_REQUIRED",
"message": "Authentication is required to access this resource",
"docs_url": "https://api.example.com/docs/authentication",
"request_id": "req_8jH2kL5m"
}
}
403 Forbidden - Insufficient Permissions
{
"error": {
"code": "INSUFFICIENT_PERMISSIONS",
"message": "You don't have permission to delete this user",
"required_role": "admin",
"request_id": "req_9pQ4rS7t"
}
}
404 Not Found - Resource Missing
{
"error": {
"code": "USER_NOT_FOUND",
"message": "User with ID 99999 was not found",
"request_id": "req_3nM7kP2q"
}
}
409 Conflict - Duplicate Resource
{
"error": {
"code": "DUPLICATE_EMAIL",
"message": "A user with this email already exists",
"field": "email",
"request_id": "req_5tY8uV1w"
}
}
422 Unprocessable Entity - Business Logic Error
{
"error": {
"code": "INSUFFICIENT_INVENTORY",
"message": "Cannot process order: insufficient inventory",
"details": [
{"product_id": 123, "requested": 10, "available": 3}
],
"request_id": "req_7wX2yZ4a"
}
}
429 Too Many Requests - Rate Limited
{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Please slow down.",
"retry_after": 60,
"limit": 100,
"remaining": 0,
"reset_at": "2024-01-15T10:31:00Z",
"request_id": "req_1aB3cD5e"
}
}
500 Internal Server Error
{
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred. Please try again later.",
"request_id": "req_6fG8hI0j",
"support_url": "https://support.example.com"
}
}
502 Bad Gateway
{
"error": {
"code": "UPSTREAM_ERROR",
"message": "Unable to reach upstream service",
"request_id": "req_2kL4mN6o"
}
}
503 Service Unavailable
{
"error": {
"code": "SERVICE_UNAVAILABLE",
"message": "Service is temporarily unavailable for maintenance",
"retry_after": 300,
"status_page": "https://status.example.com",
"request_id": "req_4pQ6rS8t"
}
}
Best Practices
🔒 Never Expose Stack Traces
"message": "An error occurred processing your request"
"stack": "Error at db.js:42\n at connect..."
Stack traces expose internal implementation details and potential security vulnerabilities. Log them server-side instead.
📝 Log Errors Server-Side
{ "request_id": "req_abc123", "error": "...", "stack": "...", "user_id": 456 }
{ "error": { "code": "INTERNAL_ERROR", "request_id": "req_abc123" }}
Include request_id in both logs and response so support can correlate client reports with server logs.
🎫 Provide Request IDs
"request_id": "req_2xK9mN3pQr5t"
Header: X-Request-ID: req_2xK9mN3pQr5t
Every request should have a unique ID. Return it in both the response body and headers for easy debugging.
📚 Include Documentation Links
"docs_url": "https://api.example.com/docs/errors#AUTH_REQUIRED"
Link to relevant documentation so developers can quickly understand and resolve issues.
⏱️ Include Retry Information
"retry_after": 60
Header: Retry-After: 60
For rate limits and temporary failures, tell clients when they can retry.
🌍 Use Consistent Error Codes
VALIDATION_ERROR, AUTH_REQUIRED, NOT_FOUND, RATE_LIMITED
error_1, err-validation, Error404
Use UPPER_SNAKE_CASE for error codes. Document all possible codes in your API reference.
Error Response Schema
A complete reference schema for your error responses
{
"error": {
// Required fields
"code": "string", // Machine-readable error code (UPPER_SNAKE_CASE)
"message": "string", // Human-readable message
// Optional fields
"request_id": "string", // Unique request identifier for debugging
"details": [ // Field-level errors (for validation)
{
"field": "string", // Field name (dot notation for nested)
"code": "string", // Field-specific error code
"message": "string" // Field-specific message
}
],
"docs_url": "string", // Link to error documentation
"retry_after": "number", // Seconds until client can retry
"support_url": "string" // Link to support/status page
}
}