jstedfast/MailKit · error · SaslException
MissingChallenge
MissingChallenge
Error message
Server response did not contain any authentication data.
What it means
SCRAM (SCRAM-SHA-1/256/512) is a multi-step exchange: after the client-first message the server must return a server-first message containing salt, nonce, and iteration count. If Challenge() is entered in the Final state with a null token, SaslException with SaslErrorCode.MissingChallenge is thrown because no server-first data arrived.
Solutions
- Log the server's exact reply to the client-first message; an empty reply usually means the server rejected the mechanism
- Verify the server supports the specific SCRAM hash (SCRAM-SHA-1 vs SCRAM-SHA-256) via its advertised AUTH mechanisms
- Authenticate over TLS with PLAIN if the server's SCRAM support is broken
- Catch SaslException with ErrorCode == SaslErrorCode.MissingChallenge and fall back to another mechanism
Example fix
// before
client.Authenticate (new SaslMechanismScramSha256 ("user", "pass"));
// after
try {
client.Authenticate (new SaslMechanismScramSha256 ("user", "pass"));
} catch (SaslException ex) when (ex.ErrorCode == SaslErrorCode.MissingChallenge) {
client.Authenticate (new SaslMechanismPlain ("user", "pass")); // over TLS
} Defensive patterns
Strategy: try-catch
Validate before calling
// Check the mechanism is advertised before use:
if (!client.AuthenticationMechanisms.Contains ("SCRAM-SHA-256"))
useFallback = true; Try / catch
try {
client.Authenticate (new SaslMechanismScramSha256 ("user", "pass"));
} catch (SaslException ex) when (ex.ErrorCode == SaslErrorCode.MissingChallenge) {
// empty server-first message: fall back to PLAIN over TLS
} Prevention
- Verify SCRAM support in the server's advertised mechanisms
- Use TLS so fallback mechanisms are safe
- Log raw SASL exchanges when integrating with a new server
- Keep MailKit updated for SCRAM fixes
When it happens
Trigger: Calling Challenge() on a SaslMechanismScramSha1/Sha256/Sha512 when state == LoginState.Final and token is null — the server sent no data in response to the client-first message.
Common situations: Servers that advertise SCRAM but fail mid-exchange, protocol layers delivering an empty continuation, or IMAP/SMTP servers rejecting the mechanism with an empty continuation instead of an error tag.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of jstedfast/MailKit@9d3859a785 (2026-09-15).
Data as JSON: /api/errors/201a78f53e8ccc69.
Report an issue: GitHub.
Appendix: source
Thrown at MailKit/Security/SaslMechanismScramBase.cs:337
//
// Based on this, we attempt to use "tls-server-end-point" instead of "tls-unique" when available.
if (SupportsChannelBinding) {
if (TryGetChannelBindingToken (ChannelBindingKind.Endpoint, out channelBindingToken)) {
channelBindingKind = ChannelBindingKind.Endpoint;
} else if (TryGetChannelBindingToken (ChannelBindingKind.Unique, out channelBindingToken)) {
channelBindingKind = ChannelBindingKind.Unique;
} else {
channelBindingKind = ChannelBindingKind.Unknown;
}
}
input = GetChannelBindingInput (channelBindingKind, AuthorizationId);
response = Encoding.UTF8.GetBytes (input + client);
state = LoginState.Final;
break;
case LoginState.Final:
if (token == null)
throw new SaslException (MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data.");
server = Encoding.UTF8.GetString (token, startIndex, length);
var tokens = ParseServerChallenge (server);
string? salt, nonce, iterations;
int count;
if (!tokens.TryGetValue ('s', out salt))
throw new SaslException (MechanismName, SaslErrorCode.IncompleteChallenge, "Challenge did not contain a salt.");
if (!tokens.TryGetValue ('r', out nonce))
throw new SaslException (MechanismName, SaslErrorCode.IncompleteChallenge, "Challenge did not contain a nonce.");
if (!tokens.TryGetValue ('i', out iterations))
throw new SaslException (MechanismName, SaslErrorCode.IncompleteChallenge, "Challenge did not contain an iteration count.");
if (!nonce.StartsWith (cnonce!, StringComparison.Ordinal))
throw new SaslException (MechanismName, SaslErrorCode.InvalidChallenge, "Challenge contained an invalid nonce.");
View on GitHub (pinned to 9d3859a785)