jstedfast/MailKit · error · TimeoutException
Operation timed out after
Error message
Operation timed out after {readTimeout} milliseconds What it means
MailKit's NetworkStream.ReadAsync wraps the socket receive in a cancellation-linked timeout; when the read doesn't complete within readTimeout, the OperationCanceledException is converted to a TimeoutException with the readTimeout in the message, and the stream disconnects first. It's the generic 'server took too long to send data' timeout.
Solutions
- Increase the client's Timeout property (e.g. client.Timeout = 120000) to a value large enough for the slowest server response.
- Catch TimeoutException and retry the operation with a fresh connection, since MailKit disconnects the stream on timeout.
- Investigate server-side latency (queue size, greylisting on SMTP, bandwidth) if timeouts persist at reasonable values.
Example fix
// before
var client = new ImapClient();
client.Connect("imap.example.com", 993, true);
// after
var client = new ImapClient();
client.Timeout = 120_000; // 2 minutes
client.Connect("imap.example.com", 993, true); Defensive patterns
Strategy: try-catch
Try / catch
try { await op(); }
catch (TimeoutException) {
// stream already disconnected; reconnect and retry with a larger Timeout
client.Timeout = 120_000;
await ReconnectAsync();
await op();
} Prevention
- Set client.Timeout generously for slow servers or large payloads
- Catch TimeoutException on every network call and reconnect — MailKit disconnects on timeout
- Profile server latency before choosing timeout values
When it happens
Trigger: Any read on a NetworkStream-based connection (e.g. AcceptProxyConnection reading the proxy protocol header, IMAP/POP3/SMTP response reads) that exceeds the client's Timeout property; server hangs or stops responding mid-command.
Common situations: Slow or overloaded mail servers; very large responses over slow links where readTimeout is too small; firewalls silently dropping packets; debugging proxies that hold connections open.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Operation timed out after
- The POP3 server has unexpectedly disconnected.
- Specified argument was out of the range of valid values…
- The socket is not connected.
- The ImapClient is not connected.
AI-assisted analysis of jstedfast/MailKit@9d3859a785 (2026-09-15).
Data as JSON: /api/errors/33cd6a6e3f0de702.
Report an issue: GitHub.
Appendix: source
Thrown at MailKit/Net/NetworkStream.cs:193
// Capture the ReadTimeout so even if we get an exception and disconnect the socket, we still have it.
int readTimeout = ReadTimeout;
using (var timeout = new CancellationTokenSource (readTimeout)) {
using (var linked = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken, timeout.Token)) {
using (var registration = linked.Token.Register (() => tcs.TrySetCanceled (), false)) {
recv!.SetBuffer (buffer, offset, count);
recv.UserToken = tcs;
if (!Socket.ReceiveAsync (recv))
AsyncOperationCompleted (null, recv);
try {
await tcs.Task.ConfigureAwait (false);
return recv.BytesTransferred;
} catch (OperationCanceledException ex) {
Disconnect ();
if (timeout.IsCancellationRequested)
throw new TimeoutException ($"Operation timed out after {readTimeout} milliseconds", ex);
throw;
} catch (Exception ex) {
Disconnect ();
if (ex is SocketException)
throw new IOException (ex.Message, ex);
throw;
}
}
}
}
}
public override void Write (byte[] buffer, int offset, int count)
{
try {
Socket.Send (buffer, offset, count, SocketFlags.None);
} catch (SocketException ex) {
throw new IOException (ex.Message, ex);View on GitHub (pinned to 9d3859a785)