Chapter 39 · Volume V — Reference

Error Contract

39.1 Overview

All ADL error responses use a standard envelope format. This chapter documents every error code, the HTTP status mapping, error details structure, and client handling guidance.

The error contract is stable across ADL versions. Client applications can rely on consistent error codes and response shapes for predictable error handling.


39.2 Error Response Format

Standard Envelope

{
  "status": "error",
  "code": 422,
  "error_code": "VALIDATION_ERROR",
  "error_message": "Validation failed",
  "data": { ... }
}
FieldTypeDescription
statusstringAlways "error" for error responses
codeintegerHTTP status code (400-599)
error_codestringMachine-readable error identifier (uppercase snake_case)
error_messagestringHuman-readable error message
dataobjectError-specific details (structure varies by error type)

39.3 HTTP Status Codes

CodeCategoryMeaning
400Client ErrorMalformed request (missing required parameter, invalid JSON)
401AuthenticationNo valid authentication token
403AuthorizationValid token but insufficient permissions
404Not FoundResource does not exist
409ConflictUnique constraint violation, concurrent modification
422ValidationRequest validation failed
423LockedResource is locked and cannot be modified
429Rate LimitToo many requests
500Server ErrorInternal server error
502Bad GatewayUpstream service unavailable
503Service UnavailableServer temporarily unavailable

39.4 Error Codes by Category

Authentication (401)

error_codeDescriptionClient Action
INVALID_CREDENTIALSWrong username/passwordShow login error, allow retry
TOKEN_EXPIREDAccess token expiredAttempt refresh token flow, then redirect to login
TOKEN_INVALIDMalformed or tampered tokenClear token, redirect to login
TOKEN_MISSINGNo Authorization headerRedirect to login

Authorization (403)

error_codeDescriptionClient Action
FORBIDDENInsufficient permissions for operationShow “access denied”, disable action
USER_SUSPENDEDAccount suspended by adminShow suspension notice, logout
GUARD_FAILEDState machine guard rejected transitionDisable transition button, show available transitions
FIELD_RESTRICTEDWrite to restricted fieldMark field as read-only

Validation (422)

error_codeDescriptionClient Action
VALIDATION_ERROROne or more fields failed validationShow per-field errors from data
REQUIRED_FIELDRequired field missingHighlight field as required
FIELD_TYPE_MISMATCHField value wrong typeShow type error on field
FIELD_TOO_LONGString exceeds maxLengthShow character count, trim
FIELD_TOO_SHORTString below minLengthShow minimum length requirement
PATTERN_MISMATCHString doesn’t match patternShow format hint (e.g., “Email format required”)
VALUE_OUT_OF_RANGENumber outside min/maxShow valid range
INVALID_ENUM_VALUEValue not in enumShow valid options
RULE_VIOLATIONCross-field validation rule failedShow rule message at form level

State Machine (422/403)

error_codeDescriptionClient Action
INVALID_TRANSITIONTransition not available from current stateShow available transitions only
FINAL_STATERecord in terminal state, no transitions availableHide all transition buttons
NO_STATERecord has no state field valueShow error, refresh record
UNKNOWN_STATEState value not defined in state machineShow error, contact support
REQUIRED_FIELD_MISSINGTransition requires field not providedShow required fields before transition

Resource (400/404)

error_codeDescriptionClient Action
NOT_FOUNDRecord does not existShow “not found” message, redirect to list
MISSING_IDNo ID in path or bodyBug in client code
MISSING_PKComposite primary key field missingProvide all key fields

Conflict (409)

error_codeDescriptionClient Action
DUPLICATEUnique constraint violationShow “already exists” on duplicate field
CONCURRENT_MODIFICATIONRecord modified since readReload record, show conflict resolution UI

Rate Limiting (429)

error_codeDescriptionClient Action
RATE_LIMITEDToo many requestsShow retry message, respect Retry-After header

Server Error (500)

error_codeDescriptionClient Action
INTERNAL_ERRORUnexpected server errorShow error message, report to support
DATABASE_ERRORDatabase operation failedShow error, retry once
INTEGRATION_ERRORExternal service errorShow error, retry with backoff

39.5 Validation Error Details

Field-Level Errors

{
  "status": "error",
  "code": 422,
  "error_code": "VALIDATION_ERROR",
  "error_message": "Validation failed",
  "data": {
    "title": {
      "code": "required",
      "message": "Title is required"
    },
    "email": {
      "code": "pattern",
      "message": "Invalid email format"
    },
    "age": {
      "code": "min",
      "message": "Age must be at least 18"
    }
  }
}

The data object is keyed by field name. Each field error contains:

  • code — validation rule that failed
  • message — human-readable error message

Cross-Field Validation Errors

{
  "status": "error",
  "code": 422,
  "error_code": "VALIDATION_ERROR",
  "error_message": "Validation failed",
  "data": {
    "dateOrder": {
      "code": "rule_failed",
      "message": "End date must be after start date"
    },
    "budgetCheck": {
      "code": "rule_failed",
      "message": "Total budget cannot exceed $100,000"
    }
  }
}

Cross-field validation rules use the rule name as the key.


39.6 State Machine Error Details

Invalid Transition

{
  "status": "error",
  "code": 422,
  "error_code": "INVALID_TRANSITION",
  "error_message": "Transition 'publish' is not available from state 'archived'",
  "data": {
    "transition": "publish",
    "currentState": "archived",
    "availableTransitions": ["restore"]
  }
}

Guard Failed

{
  "status": "error",
  "code": 403,
  "error_code": "GUARD_FAILED",
  "error_message": "Transition guard failed: User lacks required role",
  "data": {
    "transition": "approve",
    "guard": "hasRole('approver')",
    "reason": "Missing required role: approver"
  }
}

Required Fields Missing

{
  "status": "error",
  "code": 422,
  "error_code": "REQUIRED_FIELD_MISSING",
  "error_message": "Transition requires additional fields",
  "data": {
    "transition": "approve",
    "requiredFields": ["approvalDate", "approverComments"],
    "missingFields": ["approvalDate"]
  }
}

39.7 Duplicate Error Details

{
  "status": "error",
  "code": 409,
  "error_code": "DUPLICATE",
  "error_message": "Record with this value already exists",
  "data": {
    "field": "email",
    "value": "alice@example.com",
    "constraint": "unique_email"
  }
}

39.8 Rate Limit Error Details

{
  "status": "error",
  "code": 429,
  "error_code": "RATE_LIMITED",
  "error_message": "Too many requests",
  "data": {
    "limit": 100,
    "window": "1 minute",
    "retryAfter": 45
  }
}

The retryAfter field indicates seconds to wait before retrying. Also available in the Retry-After HTTP header.


39.9 Client Handling Guidance

Global Error Interceptor

Implement a global error handler that intercepts all API responses:

async function handleResponse(response: Response) {
  if (!response.ok) {
    const error = await response.json();
    
    switch (error.code) {
      case 401:
        if (error.error_code === 'TOKEN_EXPIRED') {
          // Attempt refresh
          const refreshed = await refreshToken();
          if (refreshed) {
            // Retry original request
            return retryRequest(response);
          }
        }
        // Redirect to login
        window.location.href = '/login';
        break;
        
      case 403:
        if (error.error_code === 'USER_SUSPENDED') {
          // Show suspension notice
          showSuspensionModal();
        } else {
          // Show access denied
          showError('Access denied');
        }
        break;
        
      case 422:
        // Show validation errors
        showValidationErrors(error.data);
        break;
        
      case 429:
        // Respect rate limit
        const retryAfter = error.data.retryAfter || 60;
        await sleep(retryAfter * 1000);
        return retryRequest(response);
        
      case 500:
      case 502:
      case 503:
        // Show error, report to monitoring
        reportError(error);
        showError('Server error. Please try again.');
        break;
    }
    
    throw new APIError(error);
  }
  
  return response.json();
}

Validation Error Display

function showValidationErrors(details: Record<string, ValidationError>) {
  // Clear previous errors
  clearErrors();
  
  for (const [field, error] of Object.entries(details)) {
    const input = document.querySelector(`[name="${field}"]`);
    if (input) {
      // Show error next to field
      showFieldError(input, error.message);
    } else {
      // Cross-field validation — show at form level
      showFormError(error.message);
    }
  }
}

Retry Logic

async function retryWithBackoff(
  fn: () => Promise<Response>,
  maxAttempts: number = 3
) {
  let lastError: Error;
  
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      
      // Don't retry client errors (4xx except 429)
      if (error.code >= 400 && error.code < 500 && error.code !== 429) {
        throw error;
      }
      
      // Exponential backoff: 1s, 2s, 4s
      if (attempt < maxAttempts) {
        const delay = Math.pow(2, attempt - 1) * 1000;
        await sleep(delay);
      }
    }
  }
  
  throw lastError;
}

39.10 Integration Points

FeatureError Codes
Authentication (§29)INVALID_CREDENTIALS, TOKEN_EXPIRED, TOKEN_INVALID
Permissions (§11)FORBIDDEN, GUARD_FAILED, FIELD_RESTRICTED
Validation (§14)VALIDATION_ERROR, REQUIRED_FIELD, PATTERN_MISMATCH
State Machines (§12)INVALID_TRANSITION, FINAL_STATE, REQUIRED_FIELD_MISSING
Unique Constraints (§4)DUPLICATE
Integrations (§35)INTEGRATION_ERROR, rate limiting errors

End of Chapter 39.

End of ADL v0.3 Specification Core Chapters.

Appendixes follow.