Skip to content

Error Handling

Complete guide to API error codes and handling strategies.


Error Response Format

All API errors follow a consistent structure:

{
  "error": "error_code",
  "code": "ERROR_CODE",
  "message": "Human-readable error description",
  "details": {
    "field": "additional context"
  }
}

HTTP Status Codes

Status Meaning Description
200 OK Request succeeded
201 Created Resource created
400 Bad Request Invalid request data
401 Unauthorized Authentication failed
403 Forbidden Insufficient permissions
404 Not Found Resource not found
409 Conflict Resource conflict
413 Payload Too Large Request too large
415 Unsupported Media Type Invalid file type
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Server error
503 Service Unavailable Service temporarily down

Common Error Codes

Authentication Errors (401)

UNAUTHORIZED

{
  "error": "unauthorized",
  "code": "UNAUTHORIZED",
  "message": "Authentication required"
}

Solutions: - Include authentication header - Verify token/API key is valid - Check token hasn't expired

INVALID_CREDENTIALS

{
  "error": "invalid_credentials",
  "code": "INVALID_CREDENTIALS",
  "message": "Invalid email or password"
}

TOKEN_EXPIRED

{
  "error": "token_expired",
  "code": "TOKEN_EXPIRED",
  "message": "JWT token has expired"
}

Solution: Refresh the token using /api/v1/auth/refresh


Authorization Errors (403)

FORBIDDEN

{
  "error": "forbidden",
  "code": "FORBIDDEN",
  "message": "You don't have permission to access this resource"
}

CSRF_TOKEN_REQUIRED

{
  "error": "csrf_token_required",
  "code": "CSRF_TOKEN_REQUIRED",
  "message": "CSRF token is required for this request"
}

Solution: Get CSRF token from /api/v1/auth/csrf-token


Validation Errors (400)

VALIDATION_ERROR

{
  "error": "validation_error",
  "code": "VALIDATION_ERROR",
  "message": "Request validation failed",
  "details": {
    "field": "email",
    "message": "Invalid email format"
  }
}

MISSING_REQUIRED_FIELD

{
  "error": "missing_required_field",
  "code": "MISSING_REQUIRED_FIELD",
  "message": "Required field 'email' is missing",
  "details": {
    "field": "email"
  }
}

Resource Errors (404)

NOT_FOUND

{
  "error": "not_found",
  "code": "NOT_FOUND",
  "message": "Document with ID 'doc_abc123' not found"
}

File Upload Errors

FILE_TOO_LARGE (413)

{
  "error": "file_too_large",
  "code": "FILE_TOO_LARGE",
  "message": "File size exceeds maximum limit of 500MB"
}

INVALID_FILE_TYPE (415)

{
  "error": "invalid_file_type",
  "code": "INVALID_FILE_TYPE",
  "message": "File type not supported. Supported types: pdf, docx, txt, jpg, png"
}

Rate Limit Errors (429)

RATE_LIMIT_EXCEEDED

{
  "error": "rate_limit_exceeded",
  "code": "RATE_LIMIT_EXCEEDED",
  "message": "Rate limit exceeded. Please try again later.",
  "details": {
    "limit": 120,
    "remaining": 0,
    "reset_at": "2026-01-18T10:31:00Z"
  }
}

Solution: Wait until reset_at timestamp, then retry


Error Handling Patterns

Basic Error Handling

async function makeRequest() {
  try {
    const response = await fetch('https://api.archivus.app/api/v1/documents', {
      headers: { 'X-API-Key': apiKey }
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(`${error.code}: ${error.message}`);
    }

    return await response.json();
  } catch (error) {
    console.error('API Error:', error);
    throw error;
  }
}
def make_request():
    try:
        response = requests.get(
            'https://api.archivus.app/api/v1/documents',
            headers={'X-API-Key': api_key}
        )
        response.raise_for_status()
        return response.json()
    except requests.exceptions.HTTPError as e:
        error = response.json()
        print(f"API Error: {error['code']}: {error['message']}")
        raise

Specific Error Handling

async function uploadDocument(file) {
  try {
    const response = await client.documents.upload(file);
    return response;
  } catch (error) {
    switch (error.code) {
      case 'FILE_TOO_LARGE':
        console.error('File is too large. Max size: 500MB');
        break;
      case 'INVALID_FILE_TYPE':
        console.error('Unsupported file type');
        break;
      case 'RATE_LIMIT_EXCEEDED':
        // Wait and retry
        await sleep(error.details.reset_at);
        return uploadDocument(file);
      default:
        console.error('Unexpected error:', error);
    }
    throw error;
  }
}
def upload_document(file):
    try:
        return client.documents.upload(file)
    except APIError as e:
        if e.code == 'FILE_TOO_LARGE':
            print('File is too large. Max size: 500MB')
        elif e.code == 'INVALID_FILE_TYPE':
            print('Unsupported file type')
        elif e.code == 'RATE_LIMIT_EXCEEDED':
            # Wait and retry
            time.sleep(parse_time(e.details['reset_at']))
            return upload_document(file)
        else:
            print(f'Unexpected error: {e}')
        raise

Retry Logic with Exponential Backoff

async function makeRequestWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);

      if (response.status === 429) {
        // Rate limited - wait and retry
        const retryAfter = response.headers.get('Retry-After') || 60;
        await sleep(retryAfter * 1000);
        continue;
      }

      if (response.status >= 500) {
        // Server error - retry with exponential backoff
        const waitTime = Math.pow(2, attempt) * 1000;
        await sleep(waitTime);
        continue;
      }

      return response;
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
      await sleep(Math.pow(2, attempt) * 1000);
    }
  }
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
import time
from requests.exceptions import RequestException

def make_request_with_retry(url, headers, max_retries=3):
    for attempt in range(max_retries):
        try:
            response = requests.get(url, headers=headers)

            if response.status_code == 429:
                # Rate limited - wait and retry
                retry_after = int(response.headers.get('Retry-After', 60))
                time.sleep(retry_after)
                continue

            if response.status_code >= 500:
                # Server error - retry with exponential backoff
                wait_time = 2 ** attempt
                time.sleep(wait_time)
                continue

            response.raise_for_status()
            return response.json()

        except RequestException as e:
            if attempt == max_retries - 1:
                raise
            time.sleep(2 ** attempt)

    return None

Error Code Reference

Authentication (401)

  • UNAUTHORIZED - Authentication required
  • INVALID_CREDENTIALS - Invalid email/password
  • TOKEN_EXPIRED - JWT token expired
  • INVALID_API_KEY - Invalid or revoked API key

Authorization (403)

  • FORBIDDEN - Insufficient permissions
  • CSRF_TOKEN_REQUIRED - CSRF token missing

Validation (400)

  • VALIDATION_ERROR - Request validation failed
  • MISSING_REQUIRED_FIELD - Required field missing
  • INVALID_QUERY - Invalid search query

Not Found (404)

  • NOT_FOUND - Resource not found
  • DOCUMENT_NOT_FOUND - Document not found
  • FOLDER_NOT_FOUND - Folder not found

Conflict (409)

  • DUPLICATE_RESOURCE - Resource already exists
  • FOLDER_NOT_EMPTY - Folder contains documents

Rate Limits (429)

  • RATE_LIMIT_EXCEEDED - Too many requests

Server Errors (500)

  • INTERNAL_ERROR - Internal server error
  • SERVICE_UNAVAILABLE - Service temporarily unavailable

Best Practices

1. Always Check Status Codes

if (response.status === 401) {
  // Refresh token and retry
} else if (response.status === 429) {
  // Wait for rate limit reset
} else if (response.status >= 500) {
  // Retry with backoff
}

2. Log Errors for Debugging

console.error('API Error:', {
  code: error.code,
  message: error.message,
  details: error.details,
  timestamp: new Date().toISOString()
});

3. Provide User-Friendly Messages

const userMessages = {
  'FILE_TOO_LARGE': 'File is too large. Please upload a file smaller than 500MB.',
  'INVALID_FILE_TYPE': 'This file type is not supported. Please upload a PDF, Word, or image file.',
  'RATE_LIMIT_EXCEEDED': 'Too many requests. Please try again in a few minutes.'
};

const userMessage = userMessages[error.code] || 'An error occurred. Please try again.';
showNotification(userMessage);

Next Steps