Error Handling

Comprehensive guide to error responses and handling in ModestWear API.

Overview

ModestWear API uses standard HTTP status codes and consistent error response formats to communicate issues clearly to clients.

Response Format

Success Response

{
  "success": true,
  "data": {
    // Response data
  },
  "message": "Operation successful"
}

Error Response

{
  "success": false,
  "error": "Error message describing what went wrong",
  "code": "ERROR_CODE",
  "details": {
    // Additional error context (optional)
  }
}

HTTP Status Codes

2xx Success

Code

Name

Usage

200

OK

Successful GET, PUT, PATCH, DELETE

201

Created

Successful POST (resource created)

204

No Content

Successful DELETE (no response body)

4xx Client Errors

Code

Name

Usage

400

Bad Request

Invalid input, validation errors

401

Unauthorized

Missing or invalid authentication

403

Forbidden

Authenticated but not authorized

404

Not Found

Resource doesn’t exist

409

Conflict

Resource conflict (duplicate)

422

Unprocessable Entity

Semantic errors

429

Too Many Requests

Rate limit exceeded

5xx Server Errors

Code

Name

Usage

500

Internal Server Error

Unexpected server error

502

Bad Gateway

Upstream service error

503

Service Unavailable

Server overloaded or maintenance

504

Gateway Timeout

Upstream service timeout


Authentication Errors

401 Unauthorized

Missing Token:

{
  "detail": "Authentication credentials were not provided."
}

Invalid Token:

{
  "detail": "Token is invalid or expired",
  "code": "token_not_valid"
}

Expired Token:

{
  "detail": "Given token not valid for any token type",
  "code": "token_not_valid",
  "messages": [
    {
      "token_class": "AccessToken",
      "token_type": "access",
      "message": "Token is expired"
    }
  ]
}

403 Forbidden

Account Locked:

{
  "success": false,
  "error": "Account temporarily locked due to multiple failed attempts. Try again later.",
  "lockout": true,
  "retry_after": 900
}

Account Disabled:

{
  "success": false,
  "error": "Account is disabled. Please contact support."
}

Insufficient Permissions:

{
  "detail": "You do not have permission to perform this action."
}

Validation Errors

400 Bad Request

Missing Required Fields:

{
  "success": false,
  "error": "Email and password are required"
}

Invalid Email Format:

{
  "email": [
    "Enter a valid email address."
  ]
}

Weak Password:

{
  "success": false,
  "error": "This password is too common., This password is entirely numeric."
}

Multiple Field Errors:

{
  "email": [
    "This field is required."
  ],
  "password": [
    "This field is required."
  ],
  "phone_number": [
    "Enter a valid phone number."
  ]
}

Invalid Data Type:

{
  "quantity": [
    "A valid integer is required."
  ],
  "price": [
    "A valid number is required."
  ]
}

Out of Range:

{
  "success": false,
  "error": "Quantity must be between 1 and 100"
}

Resource Errors

404 Not Found

Product Not Found:

{
  "detail": "Not found."
}

Order Not Found:

{
  "success": false,
  "error": "Order not found"
}

User Not Found:

{
  "success": false,
  "error": "User with this email does not exist"
}

409 Conflict

Duplicate Email:

{
  "success": false,
  "error": "A user with this email already exists."
}

Duplicate Wishlist Item:

{
  "success": false,
  "error": "Item already in wishlist"
}

Duplicate SKU:

{
  "sku": [
    "Product variant with this SKU already exists."
  ]
}

Business Logic Errors

Insufficient Stock

{
  "success": false,
  "error": "Insufficient stock. Only 5 items available.",
  "available_stock": 5,
  "requested_quantity": 10
}

Empty Cart

{
  "success": false,
  "error": "Cart is empty. Add items before creating an order."
}

Order Cannot Be Cancelled

{
  "success": false,
  "error": "Order cannot be cancelled. Status: shipped",
  "current_status": "shipped",
  "cancellable_statuses": ["pending", "paid"]
}

Invalid Order Status Transition

{
  "success": false,
  "error": "Cannot transition from 'delivered' to 'pending'",
  "current_status": "delivered",
  "requested_status": "pending"
}

Payment Failed

{
  "success": false,
  "error": "Payment processing failed",
  "payment_error": "Card declined",
  "code": "card_declined"
}

Rate Limiting Errors

429 Too Many Requests

{
  "detail": "Request was throttled. Expected available in 3600 seconds.",
  "retry_after": 3600
}

Response Headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705760400
Retry-After: 3600

Server Errors

500 Internal Server Error

{
  "success": false,
  "error": "An unexpected error occurred. Please try again later.",
  "error_id": "abc123xyz789"
}

Note: Error details logged server-side, not exposed to client.

502 Bad Gateway

{
  "success": false,
  "error": "Service temporarily unavailable. Please try again."
}

503 Service Unavailable

{
  "success": false,
  "error": "Service is under maintenance. Please try again later.",
  "retry_after": 1800
}

Error Handling Best Practices

Client-Side Handling

1. Check Status Code:

if (response.status === 401) {
  // Token expired, refresh or redirect to login
  refreshToken();
} else if (response.status === 403) {
  // Forbidden, show access denied message
  showAccessDenied();
} else if (response.status === 404) {
  // Not found, show 404 page
  show404Page();
}

2. Parse Error Response:

const errorData = await response.json();
if (errorData.error) {
  showErrorMessage(errorData.error);
} else if (errorData.detail) {
  showErrorMessage(errorData.detail);
}

3. Handle Field Errors:

if (response.status === 400) {
  const errors = await response.json();
  Object.keys(errors).forEach(field => {
    showFieldError(field, errors[field][0]);
  });
}

4. Retry Logic:

async function fetchWithRetry(url, options, retries = 3) {
  try {
    const response = await fetch(url, options);
    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After');
      await sleep(retryAfter * 1000);
      return fetchWithRetry(url, options, retries - 1);
    }
    return response;
  } catch (error) {
    if (retries > 0) {
      await sleep(1000);
      return fetchWithRetry(url, options, retries - 1);
    }
    throw error;
  }
}

5. Global Error Handler:

axios.interceptors.response.use(
  response => response,
  error => {
    if (error.response.status === 401) {
      // Redirect to login
      window.location.href = '/login';
    } else if (error.response.status === 500) {
      // Show generic error
      showErrorToast('Something went wrong. Please try again.');
    }
    return Promise.reject(error);
  }
);

Server-Side Handling

1. Catch Exceptions:

try:
    # Business logic
    result = process_order(order_data)
    return Response(result, status=200)
except ValidationError as e:
    return Response({'error': str(e)}, status=400)
except PermissionDenied as e:
    return Response({'error': str(e)}, status=403)
except ObjectDoesNotExist:
    return Response({'error': 'Resource not found'}, status=404)
except Exception as e:
    logger.error(f'Unexpected error: {str(e)}')
    return Response({'error': 'Internal server error'}, status=500)

2. Custom Exception Handler:

def custom_exception_handler(exc, context):
    response = exception_handler(exc, context)
    
    if response is not None:
        response.data = {
            'success': False,
            'error': response.data.get('detail', 'An error occurred'),
            'code': exc.__class__.__name__
        }
    
    return response

3. Validation:

def validate_order_data(data):
    if not data.get('address'):
        raise ValidationError('Delivery address is required')
    
    if not data.get('items'):
        raise ValidationError('Order must contain at least one item')
    
    for item in data['items']:
        if item['quantity'] < 1:
            raise ValidationError('Quantity must be at least 1')

Error Logging

Log Levels

Level

Usage

Example

DEBUG

Development debugging

Variable values, flow

INFO

General information

User logged in, order created

WARNING

Potential issues

Failed login, low stock

ERROR

Errors that need attention

Payment failed, API error

CRITICAL

System failures

Database down, service crash

Logging Examples

import logging
logger = logging.getLogger(__name__)

# Info
logger.info(f'User {user.id} logged in from IP: {ip_address}')

# Warning
logger.warning(f'Failed login attempt for email: {email}')

# Error
logger.error(f'Payment processing failed for order {order.id}: {error}')

# Critical
logger.critical(f'Database connection lost: {error}')

What to Log

✓ Log:

  • User actions (login, logout, orders)

  • Failed authentication attempts

  • Permission denials

  • Payment transactions

  • System errors

  • Performance metrics

✗ Don’t Log:

  • Passwords (even hashed)

  • Credit card numbers

  • Full email addresses (use user ID)

  • API keys or secrets

  • Personal health information


Debugging Tips

1. Check Swagger/ReDoc

Test endpoints interactively:

2. Use Django Debug Toolbar (Development)

# settings.py
if DEBUG:
    INSTALLED_APPS += ['debug_toolbar']
    MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware']

3. Enable Verbose Logging

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'root': {
        'handlers': ['console'],
        'level': 'DEBUG',  # Show all logs
    },
}

4. Test with cURL

# Test authentication
curl -X POST https://modestwear.onrender.com/api/users/login/ \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"wrong"}' \
  -v  # Verbose output

# Test with token
curl https://modestwear.onrender.com/api/orders/cart/ \
  -H "Authorization: Bearer <token>" \
  -v

5. Check Render Logs

# View live logs
render logs -f

# Search logs
render logs | grep "ERROR"

Common Issues & Solutions

Issue: Token Expired

Error:

{
  "detail": "Token is expired",
  "code": "token_not_valid"
}

Solution:

  1. Use refresh token to get new access token

  2. Retry original request with new token

  3. If refresh fails, redirect to login

Issue: CORS Error

Error:

Access to fetch at 'https://modestwear.onrender.com/api/...' 
from origin 'http://localhost:3000' has been blocked by CORS policy

Solution:

  1. Add origin to CORS_ALLOWED_ORIGINS

  2. Ensure credentials included in request

  3. Check CORS headers in response

Issue: 502 Bad Gateway

Error:

{
  "success": false,
  "error": "Service temporarily unavailable"
}

Solution:

  1. Check if service is sleeping (Render free tier)

  2. Wait for service to wake up (~30 seconds)

  3. Implement retry logic with exponential backoff

Issue: Database Connection Error

Error:

django.db.utils.OperationalError: could not connect to server

Solution:

  1. Check DATABASE_URL is correct

  2. Verify SSL mode: ?sslmode=require

  3. Check Neon database is active

  4. Verify connection pooling settings


Error Response Examples

Complete Error Response

{
  "success": false,
  "error": "Validation failed",
  "code": "VALIDATION_ERROR",
  "details": {
    "email": ["Enter a valid email address."],
    "password": ["This field is required."]
  },
  "timestamp": "2024-01-20T16:45:00Z",
  "path": "/api/users/register/",
  "request_id": "abc123xyz789"
}

Minimal Error Response

{
  "error": "Not found"
}

Next Steps