{"record":{"id":"7783f618c42599e7","repo":"jstedfast/MailKit","slug":"the-smtp-server-has-unexpectedly-disconnected-lastresponse","errorCode":null,"errorMessage":"The SMTP server has unexpectedly disconnected: {lastResponse}","messagePattern":"The SMTP server has unexpectedly disconnected: (.+?)","errorType":"exception","errorClass":"SmtpProtocolException","httpStatus":null,"severity":"error","filePath":"MailKit/Net/Smtp/SmtpStream.cs","lineNumber":253,"sourceCode":"\t\t{\n\t\t\tAlignReadAheadBuffer (out int offset, out int count);\n\n\t\t\ttry {\n\t\t\t\tvar network = Stream as NetworkStream;\n\n\t\t\t\tcancellationToken.ThrowIfCancellationRequested ();\n\n\t\t\t\tnetwork?.Poll (SelectMode.SelectRead, cancellationToken);\n\t\t\t\tint nread = Stream.Read (input, offset, count);\n\n\t\t\t\tif (nread > 0) {\n\t\t\t\t\tlogger.LogServer (input, offset, nread);\n\t\t\t\t\tinputEnd += nread;\n\n\t\t\t\t\t// Optimization hack used by ReadResponse\n\t\t\t\t\tinput[inputEnd] = (byte) '\\n';\n\t\t\t\t} else if (lastResponse is not null) {\n\t\t\t\t\tthrow new SmtpProtocolException ($\"The SMTP server has unexpectedly disconnected: {lastResponse}\");\n\t\t\t\t} else {\n\t\t\t\t\tthrow new SmtpProtocolException (\"The SMTP server has unexpectedly disconnected.\");\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tIsConnected = false;\n\t\t\t\tthrow;\n\t\t\t}\n\n\t\t\treturn inputEnd - inputIndex;\n\t\t}\n\n\t\tasync Task<int> ReadAheadAsync (CancellationToken cancellationToken)\n\t\t{\n\t\t\tAlignReadAheadBuffer (out int offset, out int count);\n\n\t\t\ttry {\n\t\t\t\tcancellationToken.ThrowIfCancellationRequested ();\n","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/jstedfast/MailKit/blob/9d3859a7855e3e17582c07fd01972b8e262bf176/MailKit/Net/Smtp/SmtpStream.cs#L235-L271","documentation":"SmtpStream.ReadAhead throws SmtpProtocolException when the socket read returns 0 bytes (EOF) mid-protocol while a partial response (lastResponse) was already buffered. The exception message includes that partial response to aid diagnosis. It means the server terminated the TCP connection without completing the SMTP exchange.","triggerScenarios":"Server closes the socket after sending a partial reply line; connection reset between the greeting/response lines; idle timeout on the server side during a multi-line read.","commonSituations":"Server-side idle timeouts killing long-lived connections; firewalls/LBs dropping connections; SMTP server crash or restart mid-session; sending commands too fast after a 421 transient failure.","solutions":["Catch SmtpProtocolException and reconnect with a fresh SmtpClient (MailKit marks IsConnected=false)","Enable client.Timeout/keepalives and resend messages with backoff for transient disconnects","Check server logs for why it dropped the connection (timeouts, limits, TLS issues)"],"exampleFix":"// before\nawait client.SendAsync(message); // throws on server disconnect\n// after\nfor (int attempt = 0; attempt < 3; attempt++) {\n    try { await client.SendAsync(message); break; }\n    catch (SmtpProtocolException) {\n        await client.ConnectAsync(host, port, useSsl);\n        await client.AuthenticateAsync(user, pass);\n    }\n}","handlingStrategy":"retry","validationCode":"if (!client.IsConnected) { client.Connect(host, port, useSsl); client.Authenticate(user, pass); }","typeGuard":null,"tryCatchPattern":"try { client.Send(message); }\ncatch (SmtpProtocolException ex) {\n    logger.LogWarning(ex, \"Server dropped connection: {0}\", ex.Message);\n    // reconnect and retry with backoff\n}","preventionTips":["Keep idle time below server/firewall timeouts","Reconnect after any SmtpProtocolException — the session is unrecoverable","Retry with exponential backoff; transient disconnects are common"],"tags":["smtp","mailkit","network","disconnection"],"backgroundTag":"broken-pipe","analyzedSha":"9d3859a7855e3e17582c07fd01972b8e262bf176","analyzedAt":"2026-09-15T15:46:11.592Z","contentChangedAt":"2026-09-15T15:46:11.592Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}