jstedfast/MailKit · error · Pop3ProtocolException

Unexpected response from server

Error message

Unexpected response from server: {0}

What it means

During the synchronous AUTH-based data-received callback in Pop3Client, the server's response line was parsed by Pop3Engine.GetCommandStatus and classified as Pop3CommandStatus.ProtocolError, so MailKit throws Pop3ProtocolException with the raw response text embedded. This means the server sent something that is not a valid POP3 (+OK / -ERR style) status line at a point where one was required. It indicates a malformed or non-protocol-compliant server reply.

Solutions

  1. Inspect the '{0}' text in the exception — it contains the exact offending response; confirm the endpoint really speaks POP3.
  2. Verify the host/port combination (typically 110 for plain POP3, 995 for POP3S) and that no proxy is intercepting the connection.
  3. Try connecting with SecureSocketOptions.None temporarily or against a known-good server to isolate whether the server is non-compliant.
  4. Enable protocol logging (new ProtocolLogger(Console.Error)) to capture the full server conversation for the server vendor.

Example fix

// before
client.Connect("mail.example.com", 587, SecureSocketOptions.None); // port is SMTP, not POP3
// after
client.Connect("mail.example.com", 995, SecureSocketOptions.SslOnConnect);
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight: verify the endpoint speaks POP3 before use
using (var probe = new TcpClient(host, port)) {
    var greeting = ReadLine(probe.GetStream());
    if (greeting == null || !greeting.StartsWith("+OK"))
        throw new InvalidOperationException($"Not a POP3 server: {greeting}");
}

Try / catch

try {
    client.Authenticate(user, pass, cancellationToken);
} catch (Pop3ProtocolException ex) {
    logger.LogError(ex, "POP3 server sent a non-protocol response: {Response}", ex.Message);
    throw;
}

Prevention

When it happens

Trigger: Calling the synchronous authentication/data path (e.g. Authenticate or an AUTH command completing via OnDataReceived) where pop3.ReadLine returns a response that GetCommandStatus cannot map to a known Pop3CommandStatus — e.g. garbage bytes, an empty line, or a server greeting/response that is not +OK or -ERR.

Common situations: Connecting to a non-POP3 port (SMTP/IMAP server or a plain HTTP endpoint) that answers with HTML or SMTP syntax; a proxy/firewall or captive portal injecting a banner; a misbehaving or very old POP3 server emitting nonstandard replies; a server that responds to SASL challenges with unexpected text.

Related errors


AI-assisted analysis of jstedfast/MailKit@9d3859a785 (2026-09-15). Data as JSON: /api/errors/b154f590052fb94d. Report an issue: GitHub.

Appendix: source

Thrown at MailKit/Net/Pop3/Pop3Client.cs:696

			}

			void OnDataReceived (Pop3Engine pop3, Pop3Command pc, string text, CancellationToken cancellationToken)
			{
				pop3.CheckConnected ();

				while (pc.Status == Pop3CommandStatus.Continue && !mechanism.IsAuthenticated) {
					var challenge = mechanism.Challenge (text, cancellationToken);
					var buf = Encoding.ASCII.GetBytes (challenge + "\r\n");

					pop3.Stream.Write (buf, 0, buf.Length, cancellationToken);
					pop3.Stream.Flush (cancellationToken);

					var response = pop3.ReadLine (cancellationToken).TrimEnd ();
					pc.Status = Pop3Engine.GetCommandStatus (response, out text);
					pc.StatusText = text;

					if (pc.Status == Pop3CommandStatus.ProtocolError)
						throw new Pop3ProtocolException (string.Format ("Unexpected response from server: {0}", response));
				}

				AuthMessage = text;
			}

			async Task OnDataReceivedAsync (Pop3Engine pop3, Pop3Command pc, string text, CancellationToken cancellationToken)
			{
				pop3.CheckConnected ();

				while (pc.Status == Pop3CommandStatus.Continue && !mechanism.IsAuthenticated) {
					var challenge = await mechanism.ChallengeAsync (text, cancellationToken).ConfigureAwait (false);
					var buf = Encoding.ASCII.GetBytes (challenge + "\r\n");

					await pop3.Stream.WriteAsync (buf, 0, buf.Length, cancellationToken).ConfigureAwait (false);
					await pop3.Stream.FlushAsync (cancellationToken).ConfigureAwait (false);

					var response = (await pop3.ReadLineAsync (cancellationToken).ConfigureAwait (false)).TrimEnd ();
					pc.Status = Pop3Engine.GetCommandStatus (response, out text);

View on GitHub (pinned to 9d3859a785)