Skip to main content
Gateway Documentation

This documents the API gateway architecture and behavior. These are not callable endpoints — they describe how the gateway processes requests.

API Gateway Error Handling

Complete reference for error responses, status codes, and error handling patterns in the Olympus Cloud API Gateway.

Overview

All API errors follow a standardized format to ensure consistent client handling across all endpoints.

Design Principles

  1. Predictable Structure - All errors use the same JSON schema
  2. Actionable Messages - Clear guidance on how to resolve issues
  3. Traceable - Request IDs and timestamps for debugging
  4. Severity Aware - Indicates urgency and retry behavior
  5. Secure - Never exposes internal implementation details

Error Response Format

Standard Error Response

All errors return this structure:

{
"error": {
"id": "err-abc123-def456",
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": "The 'email' field must be a valid email address",
"timestamp": "2026-01-19T14:30:00Z",
"request_id": "req-xyz789",
"path": "/api/v1/users",
"method": "POST",
"status_code": 400,
"severity": "low",
"retryable": false,
"errors": [
{
"field": "email",
"message": "Invalid email format",
"code": "INVALID_FORMAT",
"value": "not-an-email"
}
],
"metadata": {
"allowed_formats": ["user@example.com"]
}
}
}

Field Descriptions

FieldTypeRequiredDescription
idstringYesUnique error identifier
codestringYesMachine-readable error code
messagestringYesHuman-readable error summary
detailsstringNoExtended explanation
timestampstringYesISO 8601 error timestamp
request_idstringYesRequest tracking ID
pathstringYesRequest path
methodstringYesHTTP method
status_codeintegerYesHTTP status code
severitystringYesError severity level
retryablebooleanYesWhether request can be retried
errorsarrayNoField-level validation errors
metadataobjectNoAdditional context

Lightweight Error Format (Middleware)

Authentication middleware returns a simplified format:

{
"error": "Unauthorized",
"code": "UNAUTHORIZED",
"message": "Authentication is required to access this resource.",
"request_id": "req-xyz789",
"timestamp": "2026-01-19T14:30:00Z"
}

Error Codes Reference

Authentication Errors (401)

CodeHTTPMessageRetryable
UNAUTHORIZED401Authentication requiredNo
INVALID_TOKEN401Token validation failedNo
TOKEN_EXPIRED401Authentication token has expiredYes*
INVALID_CREDENTIALS401Invalid username or passwordNo
SESSION_EXPIRED401Session has expiredNo
SESSION_REVOKED401Session was terminatedNo

*Retryable after token refresh

Authorization Errors (403)

CodeHTTPMessageRetryable
FORBIDDEN403Access deniedNo
INSUFFICIENT_PERMISSIONS403Missing required permissionsNo
SHELL_ACCESS_DENIED403Not authorized for this shellNo
STEP_UP_REQUIRED403Additional authentication requiredYes*
MFA_REQUIRED403Multi-factor authentication requiredYes*

*Retryable after completing authentication step

Validation Errors (400)

CodeHTTPMessageRetryable
BAD_REQUEST400Invalid requestNo
VALIDATION_ERROR400Request validation failedNo
MISSING_FIELD400Required field is missingNo
INVALID_FORMAT400Field format is invalidNo
INVALID_VALUE400Field value is not allowedNo
INVALID_JSON400Request body is not valid JSONNo
CONTENT_TOO_LARGE413Request body exceeds size limitNo
UNSUPPORTED_MEDIA_TYPE415Content-Type not supportedNo

Resource Errors (404/409)

CodeHTTPMessageRetryable
NOT_FOUND404Resource not foundNo
ALREADY_EXISTS409Resource already existsNo
CONFLICT409Resource state conflictYes*
RESOURCE_LOCKED409Resource is lockedYes
DUPLICATE_ENTRY409Duplicate value detectedNo

*Retryable after resolving conflict

Business Logic Errors (409)

CodeHTTPMessageRetryable
BUSINESS_LOGIC_ERROR409Business rule violationNo
INSUFFICIENT_FUNDS409Insufficient balanceNo
ORDER_NOT_CANCELABLE409Order cannot be cancelledNo
TABLE_OCCUPIED409Table is currently occupiedYes
MENU_ITEM_UNAVAILABLE409Menu item is 86'dNo
INVENTORY_INSUFFICIENT409Not enough inventoryNo
RESERVATION_CONFLICT409Time slot unavailableYes

Service Errors (5xx)

Retryable Errors

All 5xx errors and 429 rate limit errors are retryable. Use exponential backoff for 5xx errors and respect the Retry-After header for 429 responses. Non-retryable errors (most 4xx) indicate a problem with the request itself and should not be retried without modification.

CodeHTTPMessageRetryable
INTERNAL_SERVER_ERROR500An unexpected error occurredYes
BAD_GATEWAY502Upstream service errorYes
SERVICE_UNAVAILABLE503Service temporarily unavailableYes
TIMEOUT504Request timed outYes
RATE_LIMIT_EXCEEDED429Too many requestsYes*

*Retryable after waiting for rate limit reset


Severity Levels

SeverityDescriptionClient Action
lowValidation errors, missing resourcesFix request and retry
mediumConflicts, rate limitsWait and retry or fix conflict
highAuthentication, authorization failuresRe-authenticate or escalate
criticalInternal server errorsReport to support

Validation Error Details

When validation fails, the errors array contains field-level details:

{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"errors": [
{
"field": "email",
"message": "Must be a valid email address",
"code": "INVALID_FORMAT",
"value": "not-valid"
},
{
"field": "phone",
"message": "Phone number is required",
"code": "MISSING_FIELD",
"value": null
},
{
"field": "age",
"message": "Must be 18 or older",
"code": "INVALID_VALUE",
"value": 16
}
]
}
}

Field Error Structure

FieldTypeDescription
fieldstringJSON path to the invalid field
messagestringHuman-readable validation message
codestringValidation error code
valueanyThe invalid value (sanitized)

Nested Field Paths

For nested objects, field paths use dot notation:

{
"errors": [
{
"field": "address.zip_code",
"message": "Invalid ZIP code format"
},
{
"field": "items[0].quantity",
"message": "Quantity must be positive"
}
]
}

Rate Limiting Errors

When rate limits are exceeded:

{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please slow down.",
"details": "Rate limit: 100 requests per minute",
"severity": "medium",
"retryable": true,
"metadata": {
"limit": 100,
"window": "1m",
"retry_after": 45,
"reset_at": "2026-01-19T14:31:00Z"
}
}
}

Rate Limit Headers

Rate limit information is also provided in response headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1737296460
Retry-After: 45

Service-Specific Errors

Commerce Service

{
"error": {
"code": "ORDER_NOT_CANCELABLE",
"message": "Order cannot be cancelled",
"details": "Order has already been sent to kitchen",
"metadata": {
"order_id": "order-12345",
"order_status": "preparing",
"cancelable_statuses": ["open", "pending"]
}
}
}

Inventory Service

{
"error": {
"code": "INVENTORY_INSUFFICIENT",
"message": "Insufficient inventory for order",
"details": "Not enough 'Chicken Breast' in stock",
"metadata": {
"item_id": "inv-chicken",
"requested": 5,
"available": 2,
"unit": "lb"
}
}
}

Payment Service

{
"error": {
"code": "PAYMENT_DECLINED",
"message": "Payment was declined",
"details": "Card declined by issuer",
"metadata": {
"decline_code": "insufficient_funds",
"last_four": "4242",
"card_brand": "visa"
}
}
}

Error Handling Best Practices

Client-Side Handling

TypeScript Example

interface APIError {
error: {
id: string;
code: string;
message: string;
details?: string;
timestamp: string;
request_id: string;
status_code: number;
severity: 'low' | 'medium' | 'high' | 'critical';
retryable: boolean;
errors?: FieldError[];
metadata?: Record<string, unknown>;
};
}

interface FieldError {
field: string;
message: string;
code: string;
value?: unknown;
}

async function handleAPIError(error: APIError): Promise<void> {
const { code, severity, retryable, errors } = error.error;

switch (code) {
case 'TOKEN_EXPIRED':
await refreshToken();
break;

case 'VALIDATION_ERROR':
displayFieldErrors(errors);
break;

case 'RATE_LIMIT_EXCEEDED':
const retryAfter = error.error.metadata?.retry_after;
await delay(retryAfter * 1000);
break;

case 'STEP_UP_REQUIRED':
redirectToMFA();
break;

default:
if (severity === 'critical') {
reportToErrorTracking(error);
}
displayGenericError(error.error.message);
}
}

Retry Logic

async function fetchWithRetry<T>(
url: string,
options: RequestInit,
maxRetries = 3
): Promise<T> {
let lastError: APIError | null = null;

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

if (response.ok) {
return response.json();
}

const error: APIError = await response.json();
lastError = error;

// Don't retry non-retryable errors
if (!error.error.retryable) {
throw error;
}

// Handle rate limiting
if (error.error.code === 'RATE_LIMIT_EXCEEDED') {
const retryAfter = error.error.metadata?.retry_after || 60;
await delay(retryAfter * 1000);
continue;
}

// Exponential backoff for other retryable errors
await delay(Math.pow(2, attempt) * 1000);

} catch (networkError) {
// Network errors are retryable
await delay(Math.pow(2, attempt) * 1000);
}
}

throw lastError || new Error('Max retries exceeded');
}

Logging and Debugging

Always Log the request_id

Every error response includes a unique request_id field. Always log this value on the client side. When contacting support, providing the request_id and timestamp allows the operations team to quickly locate the exact request in distributed traces and logs.

Always log the request_id for support escalation:

console.error('API Error:', {
requestId: error.error.request_id,
code: error.error.code,
message: error.error.message,
timestamp: error.error.timestamp
});

When contacting support, provide:

  • request_id from the error response
  • timestamp of the error
  • Request method and path
  • Any relevant context

HTTP Status Code Summary

StatusCategoryDescription
400Client ErrorBad request, validation failed
401Client ErrorAuthentication required
403Client ErrorPermission denied
404Client ErrorResource not found
405Client ErrorMethod not allowed
409Client ErrorConflict or business rule violation
413Client ErrorPayload too large
415Client ErrorUnsupported media type
422Client ErrorUnprocessable entity
429Client ErrorRate limit exceeded
500Server ErrorInternal server error
502Server ErrorBad gateway
503Server ErrorService unavailable
504Server ErrorGateway timeout

Troubleshooting Guide

Common Error Scenarios

"INVALID_TOKEN" After App Update

Cause: Token format changed in new version.

Solution: Clear stored tokens and re-authenticate.

localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
redirectToLogin();

"RATE_LIMIT_EXCEEDED" During Bulk Operations

Cause: Too many requests in short period.

Solution: Implement request batching and throttling.

const batchSize = 10;
const delayMs = 1000;

for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
await Promise.all(batch.map(processItem));
await delay(delayMs);
}

"SERVICE_UNAVAILABLE" Intermittently

Cause: Backend service scaling or deployment.

Solution: Implement retry with exponential backoff.

"CONFLICT" on Resource Update

Cause: Another request modified the resource.

Solution: Fetch latest resource and merge changes.

try {
await updateResource(id, changes);
} catch (error) {
if (error.error.code === 'CONFLICT') {
const latest = await getResource(id);
const merged = mergeChanges(latest, changes);
await updateResource(id, merged);
}
}