jstedfast/MailKit · error · ServiceNotConnectedException
The Pop3Client must be connected before you can…
Error message
The Pop3Client must be connected before you can authenticate.
What it means
Pop3Client.CheckCanAuthenticate validates preconditions for authentication and throws ServiceNotConnectedException when engine.IsConnected is false, with this message. Authentication is a protocol exchange over an open connection, so it requires a live TCP/TLS session first. This guard fires before any AUTH bytes are sent.
Solutions
- Call Connect (and upgrade with StartTls if needed) before calling Authenticate.
- Check client.IsConnected immediately before Authenticate; reconnect if false.
- On connection failures or dropped sessions, recreate or reconnect the Pop3Client before retrying authentication.
- Ensure the previous Connect call's exceptions are not being swallowed, leaving the client unconnected.
Example fix
// before
var client = new Pop3Client();
client.Authenticate("user", "pass"); // throws ServiceNotConnectedException
// after
var client = new Pop3Client();
client.Connect("pop.example.com", 995, SecureSocketOptions.SslOnConnect);
client.Authenticate("user", "pass", cancellationToken); Defensive patterns
Strategy: try-catch
Validate before calling
if (!client.IsConnected)
client.Connect(host, port, SecureSocketOptions.SslOnConnect); Try / catch
try {
client.Authenticate(user, pass, cancellationToken);
} catch (ServiceNotConnectedException) {
client.Connect(host, port, SecureSocketOptions.SslOnConnect);
client.Authenticate(user, pass, cancellationToken);
} Prevention
- Enforce an EnsureConnectedAndAuthenticated helper used by all mail operations.
- Never call Authenticate without a preceding successful Connect in the same code path.
- Handle dropped connections by reconnecting before re-authenticating.
- Don't swallow exceptions from Connect — they leave the client unconnected for later calls.
When it happens
Trigger: Calling Authenticate (or an overload going through CheckCanAuthenticate/saslUri) on a Pop3Client that was never connected, after Disconnect, or after the connection was dropped/disposed — i.e. engine.IsConnected is false at authenticate time.
Common situations: Calling Authenticate before Connect; an earlier Connect failed or threw and the code continued; the server dropped the connection (idle timeout, server restart) and the app tries to re-authenticate without reconnecting; reusing a disposed Pop3Client instance.
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.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- The Pop3Client has not been authenticated.
- The Pop3Client is already authenticated.
- Unexpected greeting from server
- Unknown URI scheme.
- The uri must be absolute.
AI-assisted analysis of jstedfast/MailKit@9d3859a785 (2026-09-15).
Data as JSON: /api/errors/f7a00620ea43abf2.
Report an issue: GitHub.
Appendix: source
Thrown at MailKit/Net/Pop3/Pop3Client.cs:776
try {
// Note: We defer throwing exceptions on command failure so that our caller can continue trying other authentication mechanisms.
await Engine.RunAsync (false, cancellationToken).ConfigureAwait (false);
} finally {
client.detector.IsAuthenticating = false;
}
return pc;
}
}
Uri CheckCanAuthenticate (SaslMechanism mechanism, CancellationToken cancellationToken)
{
if (mechanism == null)
throw new ArgumentNullException (nameof (mechanism));
if (!engine.IsConnected)
throw new ServiceNotConnectedException ("The Pop3Client must be connected before you can authenticate.");
if (IsAuthenticated)
throw new InvalidOperationException ("The Pop3Client is already authenticated.");
CheckDisposed ();
cancellationToken.ThrowIfCancellationRequested ();
return new Uri ("pop://" + engine.Uri.Host);
}
SaslAuthContext GetSaslAuthContext (SaslMechanism mechanism, Uri saslUri)
{
mechanism.ChannelBindingContext = engine.Stream!.Stream as IChannelBindingContext;
mechanism.Uri = saslUri;
return new SaslAuthContext (this, mechanism);
}View on GitHub (pinned to 9d3859a785)