{"record":{"id":"060b877cffb82532","repo":"jstedfast/MailKit","slug":"operation-timed-out-after-writetimeout-milliseconds","errorCode":null,"errorMessage":"Operation timed out after {writeTimeout} milliseconds","messagePattern":"Operation timed out after (.+?) milliseconds","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"MailKit/Net/NetworkStream.cs","lineNumber":238,"sourceCode":"\n\t\t\t// Capture the WriteTimeout so even if we get an exception and disconnect the socket, we still have it.\n\t\t\tint writeTimeout = WriteTimeout;\n\n\t\t\tusing (var timeout = new CancellationTokenSource (writeTimeout)) {\n\t\t\t\tusing (var linked = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken, timeout.Token)) {\n\t\t\t\t\tusing (var registration = linked.Token.Register (() => tcs.TrySetCanceled (), false)) {\n\t\t\t\t\t\tsend!.SetBuffer (buffer, offset, count);\n\t\t\t\t\t\tsend.UserToken = tcs;\n\n\t\t\t\t\t\tif (!Socket.SendAsync (send))\n\t\t\t\t\t\t\tAsyncOperationCompleted (null, send);\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait tcs.Task.ConfigureAwait (false);\n\t\t\t\t\t\t} catch (OperationCanceledException ex) {\n\t\t\t\t\t\t\tDisconnect ();\n\t\t\t\t\t\t\tif (timeout.IsCancellationRequested)\n\t\t\t\t\t\t\t\tthrow new TimeoutException ($\"Operation timed out after {writeTimeout} milliseconds\", ex);\n\t\t\t\t\t\t\tthrow;\n\t\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\t\tDisconnect ();\n\t\t\t\t\t\t\tif (ex is SocketException)\n\t\t\t\t\t\t\t\tthrow new IOException (ex.Message, ex);\n\t\t\t\t\t\t\tthrow;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic override void Flush ()\n\t\t{\n\t\t}\n\n\t\tpublic override Task FlushAsync (CancellationToken cancellationToken)\n\t\t{","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/jstedfast/MailKit/blob/9d3859a7855e3e17582c07fd01972b8e262bf176/MailKit/Net/NetworkStream.cs#L220-L256","documentation":"NetworkStream.WriteAsync mirrors the read path: if the socket send doesn't complete within writeTimeout, the stream disconnects and throws a TimeoutException with the writeTimeout in the message. This indicates the server stopped consuming data (or the network is stalled) while MailKit was writing a command or payload.","triggerScenarios":"Writing large payloads (e.g. SMTP message body, APPEND data) over a stalled connection, or any command write that exceeds the client's Timeout; the AcceptProxyConnection path writing the proxy request when the backend isn't reading.","commonSituations":"Uploading large messages on slow/upstream-limited links with a small Timeout; server stuck and not reading the socket; dead peers kept alive by half-open TCP connections.","solutions":["Increase the client's Timeout property to accommodate slow upstream bandwidth for the payload size.","Catch TimeoutException, reconnect, and resume/retry the send (MailKit has already disconnected the stream).","Check TCP stack/network health (half-open connections, MTU issues) and server read-back pressure."],"exampleFix":"// before\nsmtp.Timeout = 10_000;\nawait smtp.SendAsync(message); // large attachment, slow uplink\n\n// after\nsmtp.Timeout = 300_000; // allow 5 min for large uploads\nawait smtp.SendAsync(message);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await smtp.SendAsync(message); }\ncatch (TimeoutException) {\n    await ReconnectAsync(); // stream was disconnected mid-write\n    await smtp.SendAsync(message);\n}","preventionTips":["Scale Timeout with payload size / expected upstream bandwidth","Avoid half-open dead sockets by keeping the client's Timeout below OS TCP keepalive hangs","Retry large sends from a fresh connection"],"tags":["network","timeout","socket-write","mailkit"],"backgroundTag":"request-timeout","analyzedSha":"9d3859a7855e3e17582c07fd01972b8e262bf176","analyzedAt":"2026-09-15T15:46:11.592Z","contentChangedAt":"2026-09-15T15:46:11.592Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}