jstedfast/MailKit · error · InvalidOperationException
Untagged handlers must be registered before the command has…
Error message
Untagged handlers must be registered before the command has been queued.
What it means
RegisterUntaggedHandler must be called while the ImapCommand is still in ImapCommandStatus.Created; once the command has been queued to the engine the server may already be streaming responses, so late registration is rejected with this InvalidOperationException. It protects against silently dropping untagged responses.
Solutions
- Move all RegisterUntaggedHandler calls to immediately after ImapCommand construction and before queuing.
- If the command status is unknown, check command.Status == ImapCommandStatus.Created before registering.
- Restructure so handler choice is decided before enqueue, or build a new command instead of mutating a queued one.
Example fix
// before
engine.QueueCommand(ic, r => ...);
ic.RegisterUntaggedHandler("FETCH", handler);
// after
ic.RegisterUntaggedHandler("FETCH", handler);
engine.QueueCommand(ic, r => ...); Defensive patterns
Strategy: validation
Validate before calling
if (ic.Status != ImapCommandStatus.Created)
throw new InvalidOperationException("Cannot register untagged handler: command already queued");
ic.RegisterUntaggedHandler(atom, handler); Try / catch
try { ic.RegisterUntaggedHandler(atom, handler); } catch (InvalidOperationException) { log.Warn("Handler registered too late; rebuild command"); } Prevention
- Register handlers immediately after constructing ImapCommand, before any queue call.
- Never mutate a command after enqueueing; build a fresh one instead.
- Code-review custom IMAP command helpers for construct->register->queue ordering.
When it happens
Trigger: Calling command.RegisterUntaggedHandler(...) after QueueCommand/queueing (or after the command started running); MailKit's own Queue* helpers register handlers before queuing, so this surfaces in custom command pipelines.
Common situations: Custom IMAP extension code that constructs an ImapCommand, queues it, then tries to attach handlers conditionally; refactors that moved handler registration after an enqueue call.
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
- Compression must be enabled before selecting a folder.
- Value cannot be null. (Parameter 'name')
- Value cannot be null. (Parameter 'rights')
- Value cannot be null. (Parameter 'array')
- Specified argument was out of range of valid values…
AI-assisted analysis of jstedfast/MailKit@9d3859a785 (2026-09-15).
Data as JSON: /api/errors/3f7e14d1b4c714e8.
Report an issue: GitHub.
Appendix: source
Thrown at MailKit/Net/Imap/ImapCommand.cs:493
/// <param name="handler">The handler.</param>
/// <exception cref="System.ArgumentNullException">
/// <para><paramref name="atom"/> is <see langword="null" />.</para>
/// <para>-or-</para>
/// <para><paramref name="handler"/> is <see langword="null" />.</para>
/// </exception>
/// <exception cref="System.InvalidOperationException">
/// Untagged handlers must be registered before the command has been queued.
/// </exception>
public void RegisterUntaggedHandler (string atom, ImapUntaggedHandler handler)
{
if (atom == null)
throw new ArgumentNullException (nameof (atom));
if (handler == null)
throw new ArgumentNullException (nameof (handler));
if (Status != ImapCommandStatus.Created)
throw new InvalidOperationException ("Untagged handlers must be registered before the command has been queued.");
UntaggedHandlers.Add (atom, handler);
}
static bool IsOkNoOrBad (string atom, out ImapCommandResponse response)
{
if (atom.Equals ("OK", StringComparison.OrdinalIgnoreCase)) {
response = ImapCommandResponse.Ok;
return true;
}
if (atom.Equals ("NO", StringComparison.OrdinalIgnoreCase)) {
response = ImapCommandResponse.No;
return true;
}
if (atom.Equals ("BAD", StringComparison.OrdinalIgnoreCase)) {
response = ImapCommandResponse.Bad;View on GitHub (pinned to 9d3859a785)