jstedfast/MailKit · error · InvalidOperationException

Operation is not valid due to the current state of the…

Error message

Operation is not valid due to the current state of the object.

What it means

DIGEST-MD5 (RFC 2831) binds the digest computation to a URI (service type and host, e.g. 'imap/mail.example.com'). SaslMechanismDigestMd5.Challenge throws InvalidOperationException when the mechanism's Uri property is null because the digest response cannot be constructed without it.

Solutions

  1. Use the SaslMechanismDigestMd5(Uri uri, ...) constructor overload that supplies the service URI instead of the bare credentials constructor.
  2. Authenticate through SmtpClient/ImapClient/Pop3Client, which automatically set the mechanism's Uri from the server being connected to.
  3. Null-check Uri before calling Challenge in custom SASL drivers and construct the mechanism with the correct uri (scheme + host of the service).

Example fix

// before
var mech = new SaslMechanismDigestMd5(user, pass); // Uri is null
// after
var mech = new SaslMechanismDigestMd5(new Uri("imap://mail.example.com"), user, pass);
Defensive patterns

Strategy: validation

Validate before calling

if (mech.Uri == null)
    throw new InvalidOperationException("DIGEST-MD5 requires a service URI; use the (Uri, user, pass) constructor.");

Try / catch

try {
    client.Authenticate(uri, mech);
} catch (InvalidOperationException) {
    mech = new SaslMechanismDigestMd5(uri, user, pass);
    client.Authenticate(uri, mech);
}

Prevention

When it happens

Trigger: Instantiating SaslMechanismDigestMd5 and starting authentication without a Uri (using a constructor/property path that never sets it), then calling Challenge (directly or via Authenticate).

Common situations: Using a mechanism instance across contexts where the Uri was never provided, constructing the mechanism manually for a custom SASL host instead of via SmtpClient/ImapClient (which set the Uri from the connection).

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at MailKit/Security/SaslMechanismDigestMd5.cs:140

		/// <param name="startIndex">The index into the token specifying where the server's challenge begins.</param>
		/// <param name="length">The length of the server's challenge.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <exception cref="System.NotSupportedException">
		/// The SASL mechanism does not support SASL-IR.
		/// </exception>
		/// <exception cref="System.OperationCanceledException">
		/// The operation was canceled via the cancellation token.
		/// </exception>
		/// <exception cref="SaslException">
		/// An error has occurred while parsing the server's challenge token.
		/// </exception>
		protected override byte[]? Challenge (byte[]? token, int startIndex, int length, CancellationToken cancellationToken)
		{
			if (IsAuthenticated)
				return null;

			if (Uri is null)
				throw new InvalidOperationException ();

			switch (state) {
			case LoginState.Auth:
				if (token == null)
					throw new NotSupportedException ("DIGEST-MD5 does not support SASL-IR.");

				if (token.Length > 2048)
					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:

View on GitHub (pinned to 9d3859a785)