Error Handling
RustMailer implements a comprehensive error handling system that provides consistent, structured error responses across both REST and gRPC APIs. This guide explains how to understand and handle all error scenarios when integrating with RustMailer.
Error Response Structure
All API errors follow a consistent JSON structure:
{
"code": 30000,
"message": "Resource not found"
}
code
: A unique numeric identifier for the error typemessage
: A human-readable description of the error
Complete Error Code Reference
RustMailer defines 36 error codes organized into 7 logical categories:
Client Errors (10000-10999)
These errors indicate problems with the client request or input validation.
Code | Name | HTTP Status | gRPC Status | Description |
---|---|---|---|---|
10000 | InvalidParameter | 400 Bad Request | INVALID_ARGUMENT | Invalid request parameters or malformed data |
10010 | VRLScriptSyntaxError | 400 Bad Request | INVALID_ARGUMENT | VRL script contains syntax errors |
10020 | MissingConfiguration | 400 Bad Request | INVALID_ARGUMENT | Required configuration is missing |
10030 | Incompatible | 400 Bad Request | INVALID_ARGUMENT | Incompatible request or configuration |
10040 | ExceedsLimitation | 400 Bad Request | INVALID_ARGUMENT | Request exceeds system limitations |
10050 | EmlFileParseError | 400 Bad Request | INVALID_ARGUMENT | EML file parsing failed |
10060 | MissingContentLength | 411 Length Required | INVALID_ARGUMENT | Content-Length header is missing |
10070 | PayloadTooLarge | 413 Payload Too Large | RESOURCE_EXHAUSTED | Request payload exceeds size limits |
10080 | RequestTimeout | 408 Request Timeout | DEADLINE_EXCEEDED | Request processing timeout |
10090 | MethodNotAllowed | 405 Method Not Allowed | UNIMPLEMENTED | HTTP method not supported for endpoint |
Authentication & Authorization Errors (20000-20999)
These errors relate to authentication, authorization, and access control.
Code | Name | HTTP Status | gRPC Status | Description |
---|---|---|---|---|
20000 | PermissionDenied | 401 Unauthorized | PERMISSION_DENIED | Authentication required or invalid credentials |
20010 | AccountDisabled | 403 Forbidden | PERMISSION_DENIED | Email account is disabled |
20020 | LicenseAccountLimitReached | 403 Forbidden | PERMISSION_DENIED | Maximum licensed accounts exceeded |
20030 | LicenseExpired | 403 Forbidden | PERMISSION_DENIED | License has expired |
20040 | InvalidLicense | 403 Forbidden | PERMISSION_DENIED | License key is invalid |
20050 | OAuth2ItemDisabled | 403 Forbidden | PERMISSION_DENIED | OAuth2 configuration is disabled |
20060 | MissingRefreshToken | 500 Internal Server Error | INTERNAL | OAuth2 refresh token is missing |
Resource Errors (30000-30999)
These errors involve resource management and availability.
Code | Name | HTTP Status | gRPC Status | Description |
---|---|---|---|---|
30000 | ResourceNotFound | 404 Not Found | NOT_FOUND | Requested resource does not exist |
30010 | AlreadyExists | 409 Conflict | ALREADY_EXISTS | Resource already exists (duplicate creation) |
30020 | TooManyRequest | 429 Too Many Requests | RESOURCE_EXHAUSTED | Rate limit exceeded |
Network & Connection Errors (40000-40999)
These errors relate to network connectivity and communication issues.
Code | Name | HTTP Status | gRPC Status | Description |
---|---|---|---|---|
40000 | NetworkError | 500 Internal Server Error | INTERNAL | Network connectivity issues |
40010 | ConnectionTimeout | 500 Internal Server Error | INTERNAL | Connection timeout to external services |
40020 | ConnectionPoolTimeout | 500 Internal Server Error | INTERNAL | Database connection pool timeout |
40030 | HttpResponseError | 500 Internal Server Error | INTERNAL | HTTP response error from external service |
Email Service Errors (50000-50999)
These errors are specific to email protocol operations (IMAP/SMTP).
Code | Name | HTTP Status | gRPC Status | Description |
---|---|---|---|---|
50000 | ImapCommandFailed | 500 Internal Server Error | INTERNAL | IMAP server command failed |
50010 | ImapAuthenticationFailed | 500 Internal Server Error | INTERNAL | IMAP authentication failed |
50020 | ImapUnexpectedResult | 500 Internal Server Error | INTERNAL | IMAP server returned unexpected result |
50030 | SmtpCommandFailed | 500 Internal Server Error | INTERNAL | SMTP server command failed |
50040 | SmtpConnectionFailed | 500 Internal Server Error | INTERNAL | SMTP connection failed |
50050 | MailBoxNotCached | 500 Internal Server Error | INTERNAL | Mailbox not cached |
50060 | AutoconfigFetchFailed | 500 Internal Server Error | INTERNAL | Failed to fetch auto-configuration |
Message Queue Errors (60000-60999)
These errors relate to NATS message queue operations.
Code | Name | HTTP Status | gRPC Status | Description |
---|---|---|---|---|
60000 | NatsRequestFailed | 500 Internal Server Error | INTERNAL | NATS request failed |
60010 | NatsConnectionFailed | 500 Internal Server Error | INTERNAL | NATS connection failed |
60020 | NatsCreateStreamFailed | 500 Internal Server Error | INTERNAL | NATS stream creation failed |
System Internal Errors (70000-70999)
These are general system-level errors.
Code | Name | HTTP Status | gRPC Status | Description |
---|---|---|---|---|
70000 | InternalError | 500 Internal Server Error | INTERNAL | General internal server error |
70010 | UnhandledPoemError | 500 Internal Server Error | INTERNAL | Unhandled Poem framework error |
Error Handling Best Practices
1. Error Code Range Detection
You can quickly identify error categories by examining the error code range:
function getErrorCategory(errorCode) {
if (errorCode >= 10000 && errorCode < 20000) return 'CLIENT_ERROR';
if (errorCode >= 20000 && errorCode < 30000) return 'AUTH_ERROR';
if (errorCode >= 30000 && errorCode < 40000) return 'RESOURCE_ERROR';
if (errorCode >= 40000 && errorCode < 50000) return 'NETWORK_ERROR';
if (errorCode >= 50000 && errorCode < 60000) return 'EMAIL_SERVICE_ERROR';
if (errorCode >= 60000 && errorCode < 70000) return 'MESSAGE_QUEUE_ERROR';
if (errorCode >= 70000 && errorCode < 80000) return 'SYSTEM_ERROR';
return 'UNKNOWN_ERROR';
}
2. HTTP Status Code Handling
async function handleApiResponse(response) {
if (!response.ok) {
const errorData = await response.json();
const category = getErrorCategory(errorData.code);
switch (category) {
case 'CLIENT_ERROR':
// Handle client-side errors (fix request)
console.error('Client error:', errorData.message);
break;
case 'AUTH_ERROR':
// Handle authentication/authorization errors
redirectToLogin();
break;
case 'RESOURCE_ERROR':
// Handle resource-related errors
handleResourceError(errorData.code);
break;
default:
// Handle server errors with retry logic
scheduleRetry();
break;
}
}
}
3. gRPC Error Handling
For gRPC clients, the original RustMailer error code is available in metadata:
// gRPC client example
try {
const response = await client.someMethod(request);
} catch (error) {
const rustmailerCode = error.metadata.get('rustmailer-error-code')[0];
const category = getErrorCategory(parseInt(rustmailerCode));
handleErrorByCategory(category, error.message);
}
4. Retry Logic Implementation
const RETRYABLE_ERROR_RANGES = [
[40000, 49999], // Network errors
[50000, 59999], // Email service errors
[60000, 69999], // Message queue errors
[70000, 79999], // System errors
];
function isRetryableError(errorCode) {
return RETRYABLE_ERROR_RANGES.some(([min, max]) =>
errorCode >= min && errorCode <= max
);
}
async function apiCallWithRetry(apiCall, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await apiCall();
} catch (error) {
const errorData = await error.response.json();
if (!isRetryableError(errorData.code) || attempt === maxRetries) {
throw error;
}
// Exponential backoff
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, attempt) * 1000)
);
}
}
}
5. User-Friendly Error Messages
const ERROR_MESSAGES = {
// Client Errors
10000: "Please check your input and try again.",
10070: "The file you're trying to upload is too large.",
10080: "The request is taking too long. Please try again.",
// Auth Errors
20000: "Please log in to continue.",
20020: "You've reached your account limit. Please upgrade your plan.",
20030: "Your license has expired. Please renew to continue.",
// Resource Errors
30000: "The requested item could not be found.",
30010: "This item already exists.",
30020: "Too many requests. Please wait a moment and try again.",
// Default messages by category
'CLIENT_ERROR': "There's an issue with your request. Please check and try again.",
'AUTH_ERROR': "Authentication required. Please log in.",
'RESOURCE_ERROR': "The requested resource is not available.",
'NETWORK_ERROR': "Network connectivity issue. Please try again.",
'EMAIL_SERVICE_ERROR': "Email service temporarily unavailable.",
'MESSAGE_QUEUE_ERROR': "Message processing issue. Please try again.",
'SYSTEM_ERROR': "System temporarily unavailable. Please try again later."
};
function getUserFriendlyMessage(errorCode) {
return ERROR_MESSAGES[errorCode] ||
ERROR_MESSAGES[getErrorCategory(errorCode)] ||
"An unexpected error occurred.";
}
Framework Integration Examples
React Error Boundary
class RustMailerErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, errorInfo: null };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
if (error.response?.data?.code) {
const category = getErrorCategory(error.response.data.code);
this.setState({
errorInfo: {
code: error.response.data.code,
message: getUserFriendlyMessage(error.response.data.code),
category
}
});
}
}
render() {
if (this.state.hasError) {
return <ErrorDisplay errorInfo={this.state.errorInfo} />;
}
return this.props.children;
}
}
Axios Interceptor
axios.interceptors.response.use(
response => response,
error => {
if (error.response?.data?.code) {
const errorCode = error.response.data.code;
const category = getErrorCategory(errorCode);
// Log for debugging
console.error(`RustMailer Error [${errorCode}]:`, error.response.data.message);
// Handle specific error categories
switch (category) {
case 'AUTH_ERROR':
store.dispatch(logout());
router.push('/login');
break;
case 'RESOURCE_ERROR':
if (errorCode === 30020) { // TooManyRequest
// Implement rate limiting UI feedback
showRateLimitWarning();
}
break;
}
}
return Promise.reject(error);
}
);
Monitoring and Debugging
Error Code Metrics
Monitor error patterns by category:
// Track error frequency by category
const errorMetrics = {
CLIENT_ERROR: 0,
AUTH_ERROR: 0,
RESOURCE_ERROR: 0,
NETWORK_ERROR: 0,
EMAIL_SERVICE_ERROR: 0,
MESSAGE_QUEUE_ERROR: 0,
SYSTEM_ERROR: 0
};
function trackError(errorCode) {
const category = getErrorCategory(errorCode);
errorMetrics[category]++;
// Send to monitoring service
analytics.track('api_error', {
error_code: errorCode,
error_category: category,
timestamp: new Date().toISOString()
});
}
Debug Information
For development and debugging:
function logDetailedError(error) {
if (error.response?.data?.code) {
const errorCode = error.response.data.code;
const category = getErrorCategory(errorCode);
console.group(`🚨 RustMailer API Error [${errorCode}]`);
console.log('Category:', category);
console.log('Message:', error.response.data.message);
console.log('HTTP Status:', error.response.status);
console.log('Request URL:', error.config?.url);
console.log('Request Method:', error.config?.method);
console.groupEnd();
}
}