jstedfast/MailKit · error · SmtpProtocolException

The status codes returned by the server did not match.

Error message

The status codes returned by the server did not match.

What it means

SMTP multi-line replies must repeat the same status code on every line (e.g. 250-a / 250-b / 250 ). If consecutive lines of one reply carry different codes, ReadResponse throws SmtpProtocolException('The status codes returned by the server did not match.'), since RFC 5321 requires consistency within a single reply.

Solutions

  1. Enable protocol logging (ProtocolLogger) to capture the exact malformed multi-line reply
  2. Share one SmtpClient/stream per connection — never issue commands from multiple threads concurrently
  3. Update/replace the offending SMTP server, proxy, or security appliance
  4. Catch SmtpProtocolException and fail over to another server/connection

Example fix

// before
// two tasks sending commands on the same client concurrently
Task.Run(() => client.NoOp());
await client.SendAsync(message); // interleaved replies
// after
await _sendLock.WaitAsync();
try { await client.SendAsync(message); } finally { _sendLock.Release(); }
Defensive patterns

Strategy: try-catch

Try / catch

try
{
    await client.SendAsync(message);
}
catch (SmtpProtocolException ex) when (ex.Message.Contains("did not match"))
{
    // serialize access or switch server
}

Prevention

When it happens

Trigger: A multi-line server reply whose continuation lines have differing codes (e.g. '250-OK' followed by '450-temp'), typically from a buggy or non-compliant server/proxy.

Common situations: Broken SMTP proxies or antivirus gateways rewriting replies; servers with off-by-one bugs in multiline responses (e.g. EHLO capability lists); interleaved data from concurrent use of one connection.

Related errors


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

Appendix: source

Thrown at MailKit/Net/Smtp/SmtpStream.cs:498

		bool ReadResponse (ByteArrayBuilder builder, ref bool newLine, ref bool more, ref int code)
		{
			do {
				int startIndex = inputIndex;

				if (newLine) {
					if (inputIndex + 3 < inputEnd) {
						if (!TryParseStatusCode (input, inputIndex, out int value))
							throw new SmtpProtocolException ("Unable to parse status code returned by the server.");

						inputIndex += 3;

						if (value < 100 || !IsLegalAfterStatusCode (input[inputIndex]))
							throw new SmtpProtocolException ("Invalid status code returned by the server.");

						if (code == 0) {
							code = value;
						} else if (value != code) {
							throw new SmtpProtocolException ("The status codes returned by the server did not match.");
						}

						newLine = false;

						more = input[inputIndex] == (byte) '-';
						if (more || input[inputIndex] == (byte) ' ')
							inputIndex++;

						startIndex = inputIndex;
					} else {
						// Need input.
						return true;
					}
				}

				// Note: This depends on ReadAhead[Async] setting input[inputEnd] = '\n'
				while (input[inputIndex] != (byte) '\n')
					inputIndex++;

View on GitHub (pinned to 9d3859a785)