jstedfast/MailKit · error · ImapProtocolException

Syntax error in untagged EXPUNGE response. Unexpected…

Error message

Syntax error in untagged EXPUNGE response. Unexpected message index: 0

What it means

Thrown when an untagged EXPUNGE response reports message index 0, which is impossible: IMAP sequence numbers are 1-based. MailKit throws because subtracting 1 would yield an invalid index (-1) for OnExpunge. This is raised in the synchronous untagged-response processing path.

Solutions

  1. Inspect the protocol log to confirm the server emitted `* 0 EXPUNGE`
  2. Update or fix the IMAP server — a 1-based sequence number of 0 is a server bug per RFC 3501
  3. Upgrade MailKit; check release notes for relaxed handling of zero-valued untagged responses
  4. If a proxy rewrites sequence numbers, correct its 1-based indexing
  5. Work around by wrapping folder operations in try/catch for ImapProtocolException and issuing NOOP/re-sync if your server is known-buggy

Example fix

// before
// server: * 0 EXPUNGE
// after
// server: * 1 EXPUNGE   (1-based sequence number per RFC 3501)
Defensive patterns

Strategy: try-catch

Try / catch

try {
    folder.NoOp (); // drains untagged responses, may hit EXPUNGE parse
} catch (ImapProtocolException ex) {
    logger.LogError (ex, "Server sent '* 0 EXPUNGE' - server bug");
    // resync: reopen the folder to rebuild the message index
    folder.Close (false);
    folder.Open (FolderAccess.ReadWrite);
}

Prevention

When it happens

Trigger: The server sends `* 0 EXPUNGE` while MailKit processes untagged responses during a synchronous command (e.g. after a command issued on ImapFolder, or during NoOp/Check when updates are drained).

Common situations: Known bug class in older/broken IMAP servers (similar to the Courier-IMAP `* 0 FETCH` quirk documented in MailKit issue #428); custom or in-house server implementations emitting 0-based indices; buggy proxies translating responses.

Related errors


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

Appendix: source

Thrown at MailKit/Net/Imap/ImapEngine.cs:2927

				}
			} else {
				if (uint.TryParse (atom, NumberStyles.None, CultureInfo.InvariantCulture, out uint number)) {
					// we probably have something like "* 1 EXISTS"
					token = ReadToken (cancellationToken);

					AssertToken (token, ImapTokenType.Atom, "Syntax error in untagged response. {0}", token);

					atom = (string) token.Value;

					if (ic.UntaggedHandlers.TryGetValue (atom, out handler)) {
						// the command registered an untagged handler for this atom...
						handler (this, ic, (int) number - 1, false).GetAwaiter ().GetResult ();
					} else if (folder != null) {
						if (atom.Equals ("EXISTS", StringComparison.OrdinalIgnoreCase)) {
							folder.OnExists ((int) number);
						} else if (atom.Equals ("EXPUNGE", StringComparison.OrdinalIgnoreCase)) {
							if (number == 0)
								throw UnexpectedToken ("Syntax error in untagged EXPUNGE response. Unexpected message index: 0");

							folder.OnExpunge ((int) number - 1);
						} else if (atom.Equals ("FETCH", StringComparison.OrdinalIgnoreCase)) {
							// Apparently Courier-IMAP (2004) will reply with "* 0 FETCH ..." sometimes.
							// See https://github.com/jstedfast/MailKit/issues/428 for details.
							//if (number == 0)
							//	throw UnexpectedToken ("Syntax error in untagged FETCH response. Unexpected message index: 0");

							folder.OnUntaggedFetchResponse (this, (int) number - 1, cancellationToken);
						} else if (atom.Equals ("RECENT", StringComparison.OrdinalIgnoreCase)) {
							folder.OnRecent ((int) number);
						} else {
							//Debug.WriteLine ("Unhandled untagged response: * {0} {1}", number, atom);
						}
					} else {
						//Debug.WriteLine ("Unhandled untagged response: * {0} {1}", number, atom);
					}

View on GitHub (pinned to 9d3859a785)