jstedfast/MailKit · error · SaslException

MissingChallenge

MissingChallenge

Error message

Server response did not contain any authentication data.

What it means

In the NTLM SASL mechanism's Challenge step, after sending the NEGOTIATE message the client expects the server's CHALLENGE (TYPE 2) message. If the server replies with empty/absent authentication data (token == null), SaslException with SaslErrorCode.MissingChallenge is thrown. NTLM authentication cannot continue without the server challenge.

Solutions

  1. Log the server's raw reply to the NEGOTIATE message to see whether it sent an error instead of a challenge
  2. Confirm the server truly supports NTLM (check CAPABILITY/AUTH mechanisms) and that a security layer isn't required (use TLS)
  3. Fall back to another mechanism (e.g. PLAIN over TLS) if NTLM negotiation fails
  4. Catch SaslException with ErrorCode == SaslErrorCode.MissingChallenge and retry authentication with a different mechanism

Example fix

// before
client.Authenticate (new SaslMechanismNtlm ("user", "pass")); // server aborts NTLM
// after
try {
    client.Authenticate (new SaslMechanismNtlm ("user", "pass"));
} catch (SaslException ex) when (ex.ErrorCode == SaslErrorCode.MissingChallenge) {
    client.Authenticate (new SaslMechanismLogin ("user", "pass")); // over TLS
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the server actually advertises NTLM before attempting it:
if (!client.AuthenticationMechanisms.Contains ("NTLM"))
    useFallback = true;

Try / catch

try {
    client.Authenticate (new SaslMechanismNtlm ("user", "pass"));
} catch (SaslException ex) when (ex.ErrorCode == SaslErrorCode.MissingChallenge) {
    // server sent no NTLM challenge; fall back to PLAIN over TLS
}

Prevention

When it happens

Trigger: Calling Challenge() on SaslMechanismNtlm when state == LoginState.Challenge and token is null — i.e. the server responded to the NTLM NEGOTIATE with no data (e.g. an empty continuation).

Common situations: Servers that advertise NTLM but abort the exchange, protocol servers replying with an empty continuation line instead of the base64 TYPE 2 message, or middleware stripping the challenge.

Understand the failure class

Related errors


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

Appendix: source

Thrown at MailKit/Security/SaslMechanismNtlm.cs:297

				} else {
					if ((index = userName.IndexOf ('\\')) == -1)
						index = userName.IndexOf ('/');

					if (index >= 0) {
						domain = userName.Substring (0, index);
						userName = userName.Substring (index + 1);
					}
				}
			}

			switch (state) {
			case LoginState.Negotiate:
				message = negotiate = new NtlmNegotiateMessage (domain, Workstation, OSVersion);
				state = LoginState.Challenge;
				break;
			case LoginState.Challenge:
				if (token == null)
					throw new SaslException (MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data.");

				var password = Credentials.Password;
				message = GetChallengeResponse (domain, userName, password, token, startIndex, length);
				IsAuthenticated = true;
				break;
			}

			return message?.Encode ();
		}

		NtlmAuthenticateMessage GetChallengeResponse (string domain, string userName, string password, byte[] token, int startIndex, int length)
		{
			var challenge = new NtlmChallengeMessage (token, startIndex, length);
			var authenticate = new NtlmAuthenticateMessage (negotiate!, challenge, userName, password, domain, Workstation) {
				ClientChallenge = Nonce,
				Timestamp = Timestamp
			};
			byte[]? channelBindingToken = null;

View on GitHub (pinned to 9d3859a785)