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 .Value contains the result

  • Validation failure — the input did not pass schema validation

  • Failure — the operation failed, and .Error provides 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

Property
Type
Description

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.

Property
Type
Description

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.

Property
Type
Description

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.

Property
Type
Description

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:

Property
Type
Description

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:

Property
Type
Description

MessageId

string

The message ID

Updates

IList<StatusUpdateResult>

Ordered list of status updates

StatusInfo

Returned by IChannelConnector.GetStatusAsync.

Property
Type
Description

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.

Property
Type
Description

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:

  1. Return valuesOperationResult<T> is the standard return type for all connector operations

  2. ExceptionsConnectorException and MessagingException are thrown for non-recoverable errors during initialization, connection testing, and send operations (caught by the base class and converted to OperationResult<T>)

  3. 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:

Class
Domain
Scope

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.

Code
Description

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.

Code
Description

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.

Code
Description

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 Twilio ApiException.Code integers

  • Telegram: TelegramService.MapTelegramErrorCode() and MapTelegramSendErrorCode() map Telegram ApiRequestException.ErrorCode integers

  • Firebase: FirebaseService.MapFirebaseErrorCode() maps Firebase MessagingErrorCode enum values

  • Facebook: Error codes are assigned directly via ConnectorException, without provider error code translation

  • SendGrid: HTTP status codes are mapped in the connector; no custom error code mapping

See the channel-specific documentation for detailed mapping tables.

Last updated