Handle exceptions and errors


Introduction

In case of an error, Smtp class by default throws an exception (like all other classes of MailBee.NET do). Although you can disable exceptions using Smtp.ThrowExceptions property if you don't like them, it's not recommended as it may hide some errors which would remain untrapped.

This guide mainly contains the technical background. For the code samples, see Exceptions and errors and TestSend example topics.

MailBee.NET provides an extremely manifold system of exceptions. This lets you programmatically catch exceptions you expect and leave other exceptions unhandled (so that if an unexpected exception occurs, you'd see the program break on it instead of erroneous behavior when this exception is caught by the handler intended for other exception, and unpredictable results occur).

The base is MailBeeException. Some of its descendants such as MailBeeInvalidArgumentException and MailBeeInvalidStateException usually indicate errors in your code and cannot be disabled even if you set Smtp.ThrowExceptions property value to false. Other exceptions can occur due to external factors like network, file I/O and data format errors.

Some exceptions provide a lot of additional information besides the human-readable message. This allows your application to respond to the exception programmatically (for instance, the application can display the text of an error message from the server or the numeric error code of a Win32 error).


Error codes and localization of error messages

All exceptions deriving from MailBeeException support MailBeeException.ErrorCode property which is another method of how you can determine which error occurred. The list of error codes is available in ErrorCodes class.

Note that some exceptions can have more than one error code associated with them (and thus an error code can be more specific to the particular error than just an exception type). See TestSend Example topic for the sample code.

Error messages associated with error codes are defined in Resources class. This class can be inherited so that you can adjust some (or all) error codes along with other strings like log file messages. For instance, you may want to translate the error messages to your local language. Of course, only MailBee.NET's own messages can be translated. Many exceptions include in their text the messages from their inner exceptions and these inner exceptions are thrown by .NET Framework itself. Their language will depend on the localization of the particular .NET Framework installation.

MailBeeException.ErrorCode property can be not the only error code associated with the given exception. Some exceptions provide additional codes specific to their context. For instance,MailBeeSmtpNegativeResponseException.ResponseCode property gets the SMTP error code of the last response from the server.


Connection-level and protocol-level exceptions

Network-related exceptions can be of two kinds:

To check if the connection is still valid after the exception, you can use Smtp.IsConnected property. Also, all exceptions which indicate the connection is no longer usable implement IMailBeeSocketMustCloseException interface (after such exceptions, Smtp.IsConnected is always false).

If the connection is valid after the exception, you can send other commands if you wish. If you don't, you should issue Smtp.Disconnect method in case if you manually called Smtp.Connect to start the SMTP session (i.e. the same procedure to follow as if no exception would have occurred at all).

In other words, a protocol-level exception does not leave any negative impact on the connection and if you caught it, you can continue as if nothing had happened.

However, please note that some servers drop the connection in addition to returning a negative reply. If this happens, you'll of course need to reconnect.


Final errors, warnings, and ErrorOccurred event

When you send a single e-mail through a single SMTP relay server, any error is a show stopper (aside from the case when SmtpServer.IgnoreLoginFailure is true and the authentication error occurs).

However, if you send through multiple servers (mailer.SmtpServers.Count > 1, assuming mailer is an Smtp instance) or send multiple e-mails with a method like Smtp.SendJobs, a single error does not yet indicate the entire operation has failed.

MailBee.NET won't throw an exception if the occurred problem either affects only a particular operation within the complex process or it can be overcome with another attempt (for instance, backup SMTP relay server was able to successfully deliver the message, the DNS server has responded at the second time, etc).

Still, to inform you that things are not perfectly good, MailBee.NET raises Smtp.ErrorOccurred event in every case when an internal exception occurs and gets caught by MailBee.NET itself. Also, MailBee.NET raises this event in case of a "real" exception which will then be re-thrown to your application.

Thus, two kinds of Smtp.ErrorOccurred events exist:

To let the application distinguish between those kinds of Smtp.ErrorOccurred events, its event argument includes ErrorEventArgs.IsFinalError property. If IsFinalError is false, this is just a warning, not a real error yet.

The meaning of warning, however, is quite relative. If you call Smtp.Send to send a single e-mail, it would be a warning if the message failed to be sent through the main relay server (but we have a backup server still to try). But if the e-mail fails completely (e.g. we have no backup server or it failed either), this will be a real error. However, in case of Smtp.SendJobs which can send multiple e-mails, even the total failure of a single e-mail won't stop the entire process and will be considered warning.

If you need to know exactly if certain e-mail in a series has been sent or not, use Smtp.MessageSentSmtp.MessageNotSent, or Smtp.FinishingJob events. We assume that the method you're using to send the series of e-mails is a method like Smtp.SendJobs or Smtp.SendMailMerge (rather than multiple calls of Smtp.Send method).

For the sample code, refer to Monitor mail merge progress and track successful and failed sendings topic.

In the event handler, you can access the exception which prevented the e-mail from being sent to all or some recipients, via Reason property of the event parameters object (for instance, SmtpMessageNotSentEventArgs.Reason in case of Smtp.MessageNotSent event and ErrorEventArgs.Reason for Smtp.ErrorOccurred event).

Also, in case of Smtp.SendJobs and similar methods, the exception objects are saved in SendMailJob.ErrorReason property of every job in Smtp.JobsFailed collection (which you can examine during or after execution of Smtp.SendJobs method). Thus, you can analyze failures even not using events at all, just by iterating through this collection.


Exceptions and async methods

When using deprecated BeginXXX/EndXXX async methods and callback functions, it's important to catch exceptions. If you don't catch exceptions, the async callback will silently terminate in case of an error. This happens because an unhandled exception which occurred on a worker thread terminates only that thread, not the entire application and its main thread. The application will never know what happened. To avoid this, all samples is this guide do catch exceptions.

The same is true for modern async/await methods (with XXXAsync names). Although they are easier to use in many aspects, not handling an exception may have the effect when the current action (such as button click) does seemingly nothing to the user.

TestSend example

The console sample below was created from the internal implementation of Smtp.TestSend method. It shows the meaning of various exceptions and error codes of MailBee.NET and how they match TestSendResult values:

using System;
using MailBee;
using MailBee.Mime;
using MailBee.Pop3Mail;
using MailBee.DnsMX;
using MailBee.SmtpMail;

class Sample
{
    static void Main(string[] args)
    {
        Smtp mailer = new Smtp();

        // To make an exception get thrown, use wrong password.
        mailer.SmtpServers.Add("smtp.here.com", "joe", "bad password");

        // Prepare a simple e-mail.
        mailer.Message.From = new EmailAddress("joe@here.com");
        mailer.Message.To.Add("bill@there.com");
        mailer.Message.Subject = "Test message";

        TestSendResult result = TestSendResult.OK;
        try
        {
            // And try to send it.
            mailer.Send();
        }
        catch (MailBeeInvalidArgumentException e)
        {
            if (e.ErrorCode == ErrorCodes.NoSender)
            {
                result = TestSendResult.NoSender;
            }
            else if (e.ErrorCode == ErrorCodes.NoRecipients)
            {
                result = TestSendResult.NoRecipients;
            }
            else if (e.ErrorCode == ErrorCodes.EmptyHostNameForDnsQuery)
            {
                result = TestSendResult.NoDomainInRecipientEmail;
            }
            else
            {
                result = TestSendResult.UnknownError;
            }
        }
        catch (MailBeeSmtpRefusedSenderException)
        {
            result = TestSendResult.BadSender;
        }
        catch (MailBeeSmtpRefusedRecipientException)
        {
            result = TestSendResult.BadRecipient;
        }
        catch (MailBeeSmtpNoAcceptedRecipientsException)
        {
            result = TestSendResult.NoAcceptedRecipients;
        }
        catch (MailBeeLoginNoCredentialsException)
        {
            result = TestSendResult.NoCredentials;
        }
        catch (MailBeeLoginNoSupportedMethodsException)
        {
            result = TestSendResult.NoSupportedAuth;
        }
        catch (MailBeeSmtpLoginBadMethodException)
        {
            result = TestSendResult.BadAuthMethod;
        }
        catch (MailBeeSmtpLoginBadCredentialsException)
        {
            result = TestSendResult.BadCredentials;
        }
        catch (MailBeePop3LoginBadCredentialsException)
        {
            result = TestSendResult.BadCredentials;
        }
        catch (MailBeeSmtpNegativeResponseException)
        {
            result = TestSendResult.NegativeSmtpResponse;
        }
        catch (MailBeePop3NegativeResponseException)
        {
            result = TestSendResult.NegativePop3Response;
        }
        catch (MailBeeDnsNameErrorException)
        {
            result = TestSendResult.NoMXRecord;
        }
        catch (MailBeeDnsProtocolException)
        {
            result = TestSendResult.DnsProtocolError;
        }
        catch (MailBeeConnectionException e)
        {
            switch (e.Protocol)
            {
                case TopLevelProtocolType.Smtp:
                    result = TestSendResult.SmtpConnectionError;
                    break;
                case TopLevelProtocolType.Dns:
                    result = TestSendResult.DnsConnectionError;
                    break;
                case TopLevelProtocolType.Pop3:
                    result = TestSendResult.Pop3ConnectionError;
                    break;
                default:
                    result = TestSendResult.UnknownError;
                    break;
            }
        }
        catch (MailBeeGetRemoteHostNameException e)
        {
            switch (e.HostProtocol)
            {
                case TopLevelProtocolType.Smtp:
                    result = TestSendResult.SmtpResolveHostError;
                    break;
                case TopLevelProtocolType.Pop3:
                    result = TestSendResult.Pop3ResolveHostError;
                    break;
                default:
                    result = TestSendResult.UnknownError;
                    break;
            }
        }
        catch (MailBeeException)
        {
            result = TestSendResult.UnknownError;
        }

        // Display the result of sending attempt.
        // For instance, if the client successfully connects with
        // the server but the server says the login or password
        // is incorrect, will display BadCredentials.
        Console.WriteLine(result.ToString());
    }
}
Imports System
Imports MailBee
Imports MailBee.Mime
Imports MailBee.Pop3Mail
Imports MailBee.DnsMX
Imports MailBee.SmtpMail

Module Module1
    Sub Main()
        Dim mailer As New Smtp()

        ' To make an exception get thrown, use wrong password.
        mailer.SmtpServers.Add("smtp.here.com", "joe", "bad password")

        ' Prepare a simple e-mail.
        mailer.Message.From = New EmailAddress("joe@here.com")
        mailer.Message.To.Add("bill@there.com")
        mailer.Message.Subject = "Test message"

        Dim result As TestSendResult = TestSendResult.OK
        Try
            ' And try to send it.
            mailer.Send()
        Catch e As MailBeeInvalidArgumentException
            If e.ErrorCode = ErrorCodes.NoSender Then
                result = TestSendResult.NoSender
            Else
                If e.ErrorCode = ErrorCodes.NoRecipients Then
                    result = TestSendResult.NoRecipients
                Else
                    If e.ErrorCode = ErrorCodes.EmptyHostNameForDnsQuery Then
                        result = TestSendResult.NoDomainInRecipientEmail
                    Else
                        result = TestSendResult.UnknownError
                    End If
                End If
            End If
        Catch e As MailBeeSmtpRefusedSenderException
            result = TestSendResult.BadSender
        Catch e As MailBeeSmtpRefusedRecipientException
            result = TestSendResult.BadRecipient
        Catch e As MailBeeSmtpNoAcceptedRecipientsException
            result = TestSendResult.NoAcceptedRecipients
        Catch e As MailBeeLoginNoCredentialsException
            result = TestSendResult.NoCredentials
        Catch e As MailBeeLoginNoSupportedMethodsException
            result = TestSendResult.NoSupportedAuth
        Catch e As MailBeeSmtpLoginBadMethodException
            result = TestSendResult.BadAuthMethod
        Catch e As MailBeeSmtpLoginBadCredentialsException
            result = TestSendResult.BadCredentials
        Catch e As MailBeePop3LoginBadCredentialsException
            result = TestSendResult.BadCredentials
        Catch e As MailBeeSmtpNegativeResponseException
            result = TestSendResult.NegativeSmtpResponse
        Catch e As MailBeePop3NegativeResponseException
            result = TestSendResult.NegativePop3Response
        Catch e As MailBeeDnsNameErrorException
            result = TestSendResult.NoMXRecord
        Catch e As MailBeeDnsProtocolException
            result = TestSendResult.DnsProtocolError
        Catch e As MailBeeConnectionException
            Select Case e.Protocol
                Case TopLevelProtocolType.Smtp
                    result = TestSendResult.SmtpConnectionError
                Case TopLevelProtocolType.Dns
                    result = TestSendResult.DnsConnectionError
                Case TopLevelProtocolType.Pop3
                    result = TestSendResult.Pop3ConnectionError
                Case Else
                    result = TestSendResult.UnknownError
            End Select
        Catch e As MailBeeGetRemoteHostNameException
            Select Case e.HostProtocol
                Case TopLevelProtocolType.Smtp
                    result = TestSendResult.SmtpResolveHostError
                Case TopLevelProtocolType.Pop3
                    result = TestSendResult.Pop3ResolveHostError
                Case Else
                    result = TestSendResult.UnknownError
            End Select
        Catch e As MailBeeException
            result = TestSendResult.UnknownError
        End Try

        ' Display the result of sending attempt.
        ' For instance, if the client successfully connects with
        ' the server but the server says the login or password
        ' is incorrect, will display BadCredentials.
        Console.WriteLine(result.ToString())
    End Sub
End Module

This code above is for demonstration purposes only, you don't need it for testing e-mail addresses as you can use Smtp.TestSend method itself (or EmailAddressValidator component if you need to verify lots of addresses).

You can find more examples of exception processing with MailBee.NET in Exceptions and errorsGet DNS MX recordsSend single e-mail asynchronously topics.


Send feedback to AfterLogic

Copyright © 2006-2017 AfterLogic Corporation. All rights reserved.