jstedfast/MailKit · error · SaslException
IncompleteChallenge
IncompleteChallenge
Error message
Server response contained incomplete authentication data.
What it means
In the DIGEST-MD5 final step, the server's response must be a key/value pair whose key is 'rspauth'; otherwise the response is incomplete. SaslMechanismDigestMd5.Challenge throws SaslException with code SaslErrorCode.IncompleteChallenge when TryParseKeyValuePair fails to parse the server data.
Solutions
- Verify the server's final response format against RFC 2831; fix or report the server implementation.
- Capture a network trace of the raw final response to identify the malformed content and its source (proxy/middleware).
- Catch SaslException with ErrorCode == SaslErrorCode.IncompleteChallenge and fall back to a simpler mechanism (PLAIN/CRAM-MD5 over TLS).
Example fix
// before
client.Authenticate(uri, new SaslMechanismDigestMd5(uri, user, pass)); // server sends malformed final message
// after
try {
client.Authenticate(uri, new SaslMechanismDigestMd5(uri, user, pass));
} catch (SaslException ex) when (ex.ErrorCode == SaslErrorCode.IncompleteChallenge) {
client.Authenticate(uri, new SaslMechanismCramMd5(user, pass));
} Defensive patterns
Strategy: try-catch
Validate before calling
// validate server compliance out-of-band; at runtime guard the exchange:
try { /* authenticate */ } catch (SaslException ex) when (ex.ErrorCode == SaslErrorCode.IncompleteChallenge) { /* fallback */ } Try / catch
try {
client.Authenticate(uri, new SaslMechanismDigestMd5(uri, user, pass));
} catch (SaslException ex) when (ex.ErrorCode == SaslErrorCode.IncompleteChallenge) {
// server's final response was not a parseable rspauth pair
client.Authenticate(uri, new SaslMechanismCramMd5(user, pass));
} Prevention
- Capture a network trace of the final DIGEST-MD5 message when parsing fails
- Switch to SCRAM mechanisms to avoid DIGEST-MD5 parsing quirks
- Always filter SaslException by ErrorCode before choosing a fallback
When it happens
Trigger: Server's final DIGEST-MD5 message is not a parseable 'key=value' pair — e.g. empty garbage, a base64 blob, an error string, or a message missing the '=' separator.
Common situations: Non-conformant servers appending extra text after rspauth, servers sending error text where the final response belongs, protocol confusion where the server replies with a different SASL mechanism's message.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- MissingChallenge
- Operation is not valid due to the current state of the…
- DIGEST-MD5 does not support SASL-IR.
- ChallengeTooLong
- InvalidChallenge
AI-assisted analysis of jstedfast/MailKit@9d3859a785 (2026-09-15).
Data as JSON: /api/errors/aa5b2e59de11800a.
Report an issue: GitHub.
Appendix: source
Thrown at MailKit/Security/SaslMechanismDigestMd5.cs:166
throw new SaslException (MechanismName, SaslErrorCode.ChallengeTooLong, "Server challenge too long.");
challenge = DigestChallenge.Parse (Encoding.UTF8.GetString (token, startIndex, length));
encoding = challenge.Charset != null ? Encoding.UTF8 : TextEncodings.Latin1;
cnonce ??= GenerateEntropy (15);
response = new DigestResponse (challenge, encoding, Uri.Scheme, Uri.DnsSafeHost, AuthorizationId, Credentials.UserName, Credentials.Password, cnonce);
state = LoginState.Final;
return response.Encode (encoding);
case LoginState.Final:
if (token == null || token.Length == 0)
throw new SaslException (MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data.");
var text = encoding!.GetString (token, startIndex, length);
string? key, value;
if (!DigestChallenge.TryParseKeyValuePair (text, out key, out value))
throw new SaslException (MechanismName, SaslErrorCode.IncompleteChallenge, "Server response contained incomplete authentication data.");
if (!key.Equals ("rspauth", StringComparison.OrdinalIgnoreCase))
throw new SaslException (MechanismName, SaslErrorCode.InvalidChallenge, "Server response contained invalid data.");
var expected = response!.ComputeHash (encoding, Credentials.Password, false);
if (value != expected)
throw new SaslException (MechanismName, SaslErrorCode.IncorrectHash, "Server response did not contain the expected hash.");
IsAuthenticated = true;
break;
}
return null;
}
/// <summary>
/// Reset the state of the SASL mechanism.
/// </summary>View on GitHub (pinned to 9d3859a785)