SMTPLIB

smtplib smtpserverdisconnected Connection Unexpectedly Closed

Why Python's smtplib raises SMTPServerDisconnected "Connection unexpectedly closed" — port/TLS mismatch, idle timeout, or a reused socket — and how to fix each.

Updated Jul 1, 2026

The short answer

smtplib.SMTPServerDisconnected: Connection unexpectedly closed is a Python error raised in getreply() when the SMTP server's socket returns no data — the server dropped the TCP connection mid-command. It is not an SMTP reply code. Common causes: a port/encryption mismatch (plain connect to a 465 implicit-TLS port, or no STARTTLS on 587), an idle/timed-out connection, or reusing a closed SMTP object. Fix the transport match and open a fresh connection.

smtplib.SMTPServerDisconnected: Connection unexpectedly closed is raised by Python's standard library, not by the mail server as an SMTP reply. It means smtplib tried to read the server's response and the socket returned nothing — the TCP connection had already been closed by the peer (or a middlebox/firewall) before the expected reply arrived.

What does "Connection unexpectedly closed" mean in smtplib?

This string comes directly from CPython's smtplib.getreply(). When the library reads a response line and gets an empty result, it concludes the socket is dead:

# Lib/smtplib.py — getreply()
if not line:
self.close()
raise SMTPServerDisconnected("Connection unexpectedly closed")

There is a closely related variant — Connection unexpectedly closed: timed out (or : [Errno ...]) — raised from the OSError branch of the same method:

except OSError as e:
self.close()
raise SMTPServerDisconnected("Connection unexpectedly closed: " + str(e))

So this is a socket-level / transport problem, not a 3-digit SMTP status. There is no RFC 5321 reply code here — the server never got to send one. (Contrast with SMTPServerDisconnected('please run connect() first') and 'Server not connected', which mean you used the SMTP object without an open connection — see the causes below.)

What causes SMTPServerDisconnected: Connection unexpectedly closed?

  1. Port / encryption mismatch. Port 465 is implicit TLS (the whole session is TLS from the first byte); port 587 (and 25) are cleartext + STARTTLS. Per RFC 8314 §3.3 both 465 and 587 are valid submission ports — 465 is not deprecated. If you open a plaintext smtplib.SMTP() to a 465 port, the server expects a TLS handshake, gets cleartext, and drops the connection → "unexpectedly closed." Likewise, calling SMTP_SSL() against a 587 STARTTLS port fails the handshake.
  2. Idle or read timeout. The : timed out variant means the socket exceeded its timeout while waiting for a reply, or the server closed an idle connection. Sentry self-hosted, Odoo, and PythonAnywhere users all hit this against slow or rate-limited relays.
  3. Reusing a stale connection. SMTP servers close idle sessions and enforce per-connection message limits. If you cache one SMTP object and reuse it for many sends, a later command hits a server that already hung up.
  4. Provider/firewall dropping the connection — cloud egress filtering, a relay blocking the source IP, or a missing EHLO/STARTTLS the server requires before it will talk.

How do I fix SMTPServerDisconnected: Connection unexpectedly closed?

1. Match the class to the port. Use implicit TLS for 465 and STARTTLS for 587:

import smtplib, ssl
ctx = ssl.create_default_context()
# Port 465 — implicit TLS (TLS from the start)
with smtplib.SMTP_SSL("smtp.example.com", 465, context=ctx, timeout=30) as s:
s.login(user, app_password)
s.send_message(msg)
# Port 587 — STARTTLS (upgrade a cleartext connection)
with smtplib.SMTP("smtp.example.com", 587, timeout=30) as s:
s.ehlo()
s.starttls(context=ctx)
s.ehlo()
s.login(user, app_password)
s.send_message(msg)

2. Open a fresh connection per send (or per batch). Don't keep a long-lived SMTP object across idle periods — the with block above closes and reopens cleanly. If you must reuse, call s.noop() and reconnect on failure.

3. Set an explicit timeout= so a stalled relay raises promptly, and add a retry/reconnect on SMTPServerDisconnected rather than silently failing.

4. For Gmail / Google Workspace, authentication failures can also surface as a dropped connection. Google disabled Less Secure Apps for personal Gmail accounts on May 30, 2022. Google Workspace phased LSA out separately, completing the shutdown by May 1, 2025. Either way, do not rely on plain username/password SMTP auth today — use an App Password (requires 2-Step Verification) or OAuth2 with smtp.gmail.com on 465 (SMTP_SSL) or 587 (starttls).

5. If transport is correct but the server still drops you, the relay is closing the connection on its side — check provider sending limits, source-IP allowlisting, and whether outbound SMTP egress is firewalled in your environment. Confirm reachability with openssl s_client -connect host:465 (or -starttls smtp -connect host:587).

FAQ

Common questions

No. It is a Python smtplib exception raised by getreply() when the socket returns no data, meaning the server closed the TCP connection before sending a reply. There is no associated RFC 5321 three-digit reply code, because the server never sent one.

One API, every provider

Stop debugging raw provider errors

Courier connects to your email, SMS, and push providers, handles retries and failover, and surfaces delivery errors in plain language.

Reply-code definitions per RFC 8314 §3.3. Last reviewed Jul 1, 2026. Courier is not affiliated with third-party providers; error behavior may vary by implementation.