Error Handling

AS2aaS provides comprehensive error handling with detailed error responses, consistent status codes, and actionable error messages to facilitate robust application integration.

Error Response Format

All API errors follow a standardized response structure:

{
  "error": {
    "type": "error_category",
    "code": "specific_error_identifier",
    "message": "Human-readable error description", 
    "details": {
      "field": "parameter_name",
      "provided": "invalid_value",
      "expected": "valid_format_description"
    }
  }
}

HTTP Status Codes

CodeStatusDescriptionUsage
200OKRequest successfulGET, PATCH, DELETE operations
201CreatedResource createdPOST operations
400Bad RequestInvalid request syntaxMalformed JSON, invalid parameters
401UnauthorizedAuthentication failedInvalid or missing API key
403ForbiddenAccess deniedInsufficient permissions
404Not FoundResource not foundInvalid resource ID
422Unprocessable EntityValidation failedBusiness logic validation errors
429Too Many RequestsRate limit exceededAPI rate limiting
500Internal Server ErrorServer errorPlatform errors

Error Categories

Authentication Errors

Invalid API Key:

{
  "error": {
    "type": "authentication_error",
    "code": "invalid_api_key",
    "message": "The provided API key is invalid or has been revoked"
  }
}

Expired API Key:

{
  "error": {
    "type": "authentication_error", 
    "code": "api_key_expired",
    "message": "The API key has expired and must be renewed"
  }
}

Authorization Errors

Insufficient Permissions:

{
  "error": {
    "type": "authorization_error",
    "code": "insufficient_scope",
    "message": "The API key does not have required permissions for this operation",
    "details": {
      "required_scope": "write",
      "current_scopes": ["read"]
    }
  }
}

IP Address Restricted:

{
  "error": {
    "type": "authorization_error",
    "code": "ip_not_allowed",
    "message": "Requests from this IP address are not permitted",
    "details": {
      "client_ip": "203.0.113.100",
      "allowed_ips": ["203.0.113.1", "203.0.113.2"]
    }
  }
}

Validation Errors

Missing Required Field:

{
  "error": {
    "type": "validation_error",
    "code": "missing_required_field",
    "message": "Required field is missing from request",
    "details": {
      "field": "partner_id",
      "provided": null,
      "expected": "string (partner identifier)"
    }
  }
}

Invalid Field Format:

{
  "error": {
    "type": "validation_error",
    "code": "invalid_field_format",
    "message": "Field value does not match expected format",
    "details": {
      "field": "email",
      "provided": "invalid-email",
      "expected": "valid email address format"
    }
  }
}

Resource Errors

Partner Not Found:

{
  "error": {
    "type": "resource_error",
    "code": "partner_not_found", 
    "message": "Specified trading partner does not exist",
    "details": {
      "partner_id": "prt_nonexistent",
      "suggestion": "Verify partner ID and ensure partner exists"
    }
  }
}

Certificate Not Found:

{
  "error": {
    "type": "resource_error",
    "code": "certificate_not_found",
    "message": "Specified certificate does not exist or is not accessible",
    "details": {
      "certificate_id": "cert_invalid",
      "suggestion": "Verify certificate ID and access permissions"
    }
  }
}

Processing Errors

Message Processing Failed:

{
  "error": {
    "type": "processing_error",
    "code": "message_processing_failed",
    "message": "Message could not be processed due to content issues",
    "details": {
      "stage": "encryption",
      "reason": "Partner encryption certificate not configured"
    }
  }
}

Transmission Failed:

{
  "error": {
    "type": "transmission_error",
    "code": "partner_endpoint_unreachable",
    "message": "Unable to connect to partner endpoint",
    "details": {
      "partner_url": "https://partner.com/as2",
      "error_details": "Connection timeout after 30 seconds"
    }
  }
}

Rate Limiting Errors

Rate Limit Exceeded:

{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded",
    "message": "API rate limit exceeded for this key",
    "details": {
      "limit": 300,
      "window": "60 seconds",
      "retry_after": 45
    }
  }
}

Error Handling Implementation

JavaScript/Node.js

class AS2ErrorHandler {
  static handleError(error, operation) {
    if (error.response) {
      const errorData = error.response.data.error;
      
      switch (errorData.code) {
        case 'rate_limit_exceeded':
          return this.handleRateLimit(error, operation);
        case 'invalid_partner':
          return this.handleInvalidPartner(errorData, operation);
        case 'certificate_expired':
          return this.handleExpiredCertificate(errorData, operation);
        default:
          return this.handleGenericError(errorData, operation);
      }
    } else if (error.request) {
      return this.handleNetworkError(error, operation);
    } else {
      return this.handleRequestError(error, operation);
    }
  }

  static async handleRateLimit(error, operation) {
    const retryAfter = error.response.headers['retry-after'] || 60;
    console.log(`Rate limited. Retrying ${operation} after ${retryAfter} seconds`);
    
    await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
    return { retry: true, delay: retryAfter };
  }

  static handleInvalidPartner(errorData, operation) {
    console.error(`Invalid partner for ${operation}:`, errorData.details);
    return { 
      retry: false, 
      action: 'verify_partner_configuration',
      suggestion: errorData.details.suggestion 
    };
  }
}

Python

import time
import logging
from typing import Dict, Any, Optional

class AS2ErrorHandler:
    @staticmethod
    def handle_api_error(error_response: Dict[str, Any], operation: str) -> Dict[str, Any]:
        """Handle AS2aaS API errors with appropriate recovery strategies"""
        
        error_data = error_response.get('error', {})
        error_code = error_data.get('code')
        
        handlers = {
            'rate_limit_exceeded': AS2ErrorHandler._handle_rate_limit,
            'invalid_partner': AS2ErrorHandler._handle_invalid_partner,
            'certificate_expired': AS2ErrorHandler._handle_expired_certificate,
            'transmission_timeout': AS2ErrorHandler._handle_transmission_timeout
        }
        
        handler = handlers.get(error_code, AS2ErrorHandler._handle_generic_error)
        return handler(error_data, operation)
    
    @staticmethod
    def _handle_rate_limit(error_data: Dict, operation: str) -> Dict[str, Any]:
        retry_after = error_data.get('details', {}).get('retry_after', 60)
        logging.warning(f"Rate limited for {operation}. Retrying after {retry_after} seconds")
        
        time.sleep(retry_after)
        return {'retry': True, 'delay': retry_after}
    
    @staticmethod
    def _handle_invalid_partner(error_data: Dict, operation: str) -> Dict[str, Any]:
        details = error_data.get('details', {})
        logging.error(f"Invalid partner for {operation}: {details}")
        
        return {
            'retry': False,
            'action': 'verify_partner_configuration',
            'suggestion': details.get('suggestion')
        }

Java

public class AS2ErrorHandler {
    private static final Logger logger = LoggerFactory.getLogger(AS2ErrorHandler.class);
    
    public static ErrorResponse handleAPIError(HttpResponse<String> response, String operation) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            Map<String, Object> errorResponse = mapper.readValue(response.body(), Map.class);
            Map<String, Object> error = (Map<String, Object>) errorResponse.get("error");
            
            String errorCode = (String) error.get("code");
            
            switch (errorCode) {
                case "rate_limit_exceeded":
                    return handleRateLimit(error, operation);
                case "invalid_partner":
                    return handleInvalidPartner(error, operation);
                case "certificate_expired":
                    return handleExpiredCertificate(error, operation);
                default:
                    return handleGenericError(error, operation);
            }
        } catch (Exception e) {
            logger.error("Failed to parse error response", e);
            return new ErrorResponse(false, "Failed to parse error response");
        }
    }
    
    private static ErrorResponse handleRateLimit(Map<String, Object> error, String operation) {
        Map<String, Object> details = (Map<String, Object>) error.get("details");
        Integer retryAfter = (Integer) details.get("retry_after");
        
        logger.warn("Rate limited for {}. Retrying after {} seconds", operation, retryAfter);
        
        try {
            Thread.sleep(retryAfter * 1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        return new ErrorResponse(true, "Rate limit handled");
    }
}

Best Practices

Error Recovery

  • Implement exponential backoff for transient errors
  • Use circuit breaker patterns for partner endpoint failures
  • Log all errors with sufficient context for debugging
  • Monitor error rates and patterns for system health

Monitoring

  • Track error rates by error type and partner
  • Set up alerts for critical error conditions
  • Implement health checks for dependent services
  • Monitor API response times and success rates

Development

  • Use test environment for error scenario testing
  • Implement comprehensive error handling in all integrations
  • Document error handling procedures for operations teams
  • Test error scenarios during integration development