Result Types
If every connector used its own error model — one throwing exceptions, another returning tuples, a third using custom error objects — the application code that calls connectors would be a mess of adapters and conditionals. The framework standardizes on a single result type for every operation: OperationResult<T>.
The result type carries three possible outcomes:
Success — the operation completed, and
.Valuecontains the resultValidation failure — the input did not pass schema validation
Failure — the operation failed, and
.Errorprovides a machine-readable code and a human-readable message
This tri-state model avoids the ambiguity of throwing exceptions for validation errors (which are not exceptional — they indicate caller bugs) and distinguishes transient provider failures from preventable input errors.
Every connector operation returns an OperationResult<T>. This gives you a consistent pattern for handling success, failure, and validation errors across all channels.
OperationResult<T>
OperationResult<T> is the standard return type for all connector operations. It comes from the Deveel.Results package.
Properties
IsSuccess()
bool
Operation completed successfully (method)
IsFailure()
bool
Operation failed (method)
Value
T?
Result value (non-null when IsSuccess)
Error
IMessagingError?
Error code and message (non-null when IsFailure)
Note: OperationResult<T> is provided by the Deveel.Results package. Use .IsSuccess() and .IsFailure() as methods.
Usage patterns
Factory methods
The implicit conversion is what makes ChannelConnectorBase overrides clean — your core methods return raw values, and the base class wraps them.
SendResult
Returned by IChannelConnector.SendMessageAsync.
MessageId
string
The local message ID you assigned
RemoteMessageId
string
Provider-assigned message identifier
Status
MessageStatus?
Initial delivery status from provider
Timestamp
DateTimeOffset?
When the provider accepted the message
AdditionalData
IDictionary<string, object>
Provider-specific metadata; includes RetryAttempts when retry policy is active
The GetRetryAttempts() extension method reads the retry count from AdditionalData:
In a connector override
BatchSendResult
Returned by IChannelConnector.SendBatchAsync.
BatchId
string
Local batch identifier
RemoteBatchId
string?
Provider-assigned batch ID (if supported)
MessageResults
IDictionary<string, SendResult>
Per-message results keyed by message ID
ReceiveResult
Returned by IChannelConnector.ReceiveMessagesAsync.
BatchId
string
Identifier for this receive batch
Messages
IReadOnlyList<IMessage>
Received messages
MessageSource
The MessageSource struct represents an inbound message payload (from webhooks, callbacks, etc.):
StatusUpdateResult / StatusUpdatesResult
Returned by IChannelConnector.ReceiveMessageStatusAsync and GetMessageStatusAsync.
StatusUpdateResult properties:
MessageId
string
The message ID
Status
MessageStatus
Status value (Received, Queued, Sent, Delivered, DeliveryFailed, etc.)
Timestamp
DateTimeOffset
When the status was recorded
Description
string?
Optional human-readable description
AdditionalData
IDictionary<string, object>
Provider-specific metadata; includes RetryAttempts when retry policy is active
StatusUpdatesResult properties:
MessageId
string
The message ID
Updates
IList<StatusUpdateResult>
Ordered list of status updates
StatusInfo
Returned by IChannelConnector.GetStatusAsync.
Status
string
Status string (provider-specific)
Description
string?
Optional description
Timestamp
DateTimeOffset
When the status was determined
AdditionalData
IDictionary<string, object>
Provider-specific metadata; includes RetryAttempts when retry policy is active
ConnectorHealth
Returned by IChannelConnector.GetHealthAsync.
State
ConnectorState
Current lifecycle state
IsHealthy
bool
true if the connector is operating normally
LastHealthCheck
DateTime
When the health check was last run
Uptime
TimeSpan
Time since last successful initialization
Metrics
Dictionary<string, object>
Custom metrics (message count, error rate, latency, etc.)
Issues
List<string>
Human-readable issue descriptions
IMessagingError
Error handling
The framework uses a three-layer error handling model:
Return values —
OperationResult<T>is the standard return type for all connector operationsExceptions —
ConnectorExceptionandMessagingExceptionare thrown for non-recoverable errors during initialization, connection testing, and send operations (caught by the base class and converted toOperationResult<T>)Error codes — Every error has a machine-readable string code and a human-readable message
ConnectorException
Thrown inside connector operations to signal a recoverable or provider-specific error. The base class catches it and wraps it in OperationResult<T>.Fail():
Error code hierarchy
Error codes are organized by scope in static classes:
MessagingErrorCodes
"messaging"
General messaging errors
ConnectorErrorCodes
"messaging"
Connector lifecycle and operation errors
FacebookErrorCodes
"Facebook"
Facebook Messenger API errors
FirebaseErrorCodes
"Firebase"
Firebase Cloud Messaging errors
TelegramErrorCodes
"Telegram"
Telegram Bot API errors
TwilioErrorCodes
"Twilio"
Twilio SMS/WhatsApp errors
SendGridErrorCodes
"SendGrid"
SendGrid email errors
ConnectorException
Thrown inside connector lifecycle methods (InitializeAsync, TestConnectionAsync, SendMessageCoreAsync, ReceiveMessagesCoreAsync, etc.) to signal a specific error. Contains an error code, a domain string, and a human-readable message.
Authentication errors
Authentication providers (AuthenticationProviderBase subclasses) return AuthenticationResult with a string error code when authentication fails. The AuthenticationManager wraps these into a ConnectorException with code AUTHENTICATION_FAILED or reports the provider-specific code directly.
Error code tables
MessagingErrorCodes (general)
These codes are used for messaging-level errors outside the scope of channel connectors, such as routing and configuration.
MESSAGING_ERROR
Unspecified or unexpected messaging error
MESSAGE_ROUTING_FAILED
Message could not be routed to the intended recipient or channel
MESSAGE_SERIALIZATION_FAILED
Message serialization failed
MESSAGE_DESERIALIZATION_FAILED
Message deserialization failed
INVALID_CONFIGURATION
Connector configuration is invalid or incomplete
UNSUPPORTED_CONTENT_TYPE
Unsupported message content type encountered
CONNECTOR_NOT_FOUND
No connector was found for the requested channel type
INVALID_WEBHOOK_DATA
Webhook data is invalid or malformed
INVALID_RECIPIENT
Recipient endpoint is missing, invalid, or unreachable
MISSING_CREDENTIALS
Required credentials are missing
INVALID_CREDENTIALS
Provided credentials are invalid or expired
MISSING_SENDER
Sender endpoint is missing or not configured
MESSAGE_TOO_LONG
Message exceeds the maximum allowed length
CONNECTION_FAILED
Connection to the remote service failed
SEND_MESSAGE_FAILED
Sending a message failed
RATE_LIMIT_EXCEEDED
API rate limit has been exceeded
ConnectorErrorCodes (connector operations)
These codes are defined in Ratatosk.Connectors and used by ChannelConnectorBase.
ALREADY_INITIALIZED
Connector has already been initialized
INITIALIZATION_ERROR
Error occurred during connector initialization
AUTHENTICATION_FAILED
Authentication with the remote service failed
CONNECTION_TEST_ERROR
Error testing connection to the external service
MESSAGE_VALIDATION_FAILED
Message validation failed before sending
SEND_MESSAGE_ERROR
Error sending a single message
BATCH_VALIDATION_FAILED
Batch validation failed before sending
SEND_BATCH_ERROR
Error sending a batch of messages
GET_STATUS_ERROR
Error retrieving connector status
GET_MESSAGE_STATUS_ERROR
Error retrieving message status
GET_HEALTH_ERROR
Error performing health check
RECEIVE_STATUS_ERROR
Error receiving status updates
RECEIVE_MESSAGES_ERROR
Error receiving messages
Authentication error codes
These codes are returned by authentication providers when obtaining or refreshing credentials fails.
MISSING_API_KEY
API key not found in connection settings
MISSING_TOKEN
Bearer token not found
MISSING_BASIC_CREDENTIALS
Basic authentication credentials (username/password) not found
MISSING_PARAMETERS
Required OAuth parameters (client ID, secret) are missing
MISSING_TOKEN_ENDPOINT
Token endpoint URL is required but missing
TOKEN_REQUEST_FAILED
Token request to the provider failed
INVALID_TOKEN_RESPONSE
Token response is missing the access token
EMPTY_ACCESS_TOKEN
Empty access token received from provider
INVALID_REFRESH_RESPONSE
Refresh response is missing the access token
EMPTY_REFRESH_TOKEN
Empty access token received from refresh
REFRESH_FAILED
Token refresh operation failed
NETWORK_ERROR
Network error during token request
TIMEOUT
Token request timed out
INVALID_JSON
Invalid JSON in provider response
UNEXPECTED_ERROR
Unexpected error during authentication
MISSING_SERVICE_ACCOUNT_KEY
Service account key is required but missing
SERVICE_ACCOUNT_FILE_NOT_FOUND
Service account key file does not exist
INVALID_SERVICE_ACCOUNT_JSON
Service account key is not valid JSON
CREDENTIAL_ERROR
Error preparing credential
NO_PROVIDER
No authentication provider available for the requested scheme
AUTHENTICATION_ERROR
Unspecified authentication error
Error code mapping
Channel connectors map provider-specific API errors to the framework's error codes through dedicated mapping methods in their service implementations:
Twilio:
TwilioService.MapTwilioErrorCode()maps TwilioApiException.CodeintegersTelegram:
TelegramService.MapTelegramErrorCode()andMapTelegramSendErrorCode()map TelegramApiRequestException.ErrorCodeintegersFirebase:
FirebaseService.MapFirebaseErrorCode()maps FirebaseMessagingErrorCodeenum valuesFacebook: Error codes are assigned directly via
ConnectorException, without provider error code translationSendGrid: HTTP status codes are mapped in the connector; no custom error code mapping
See the channel-specific documentation for detailed mapping tables.
Last updated