SMTP
Python smtplib reports each failure mode as a distinct exception. Read the traceback first, then fix credentials, port, or STARTTLS setting to resolve it.
Updated Jul 1, 2026
The short answer
"SMTP not working in Python" isn't a single error — it's a catch-all for failures in Python's smtplib. The fix depends on the exception you actually get: SMTPAuthenticationError means bad/insufficient credentials (use a Gmail App Password, not your login password), SMTPServerDisconnected/SMTPConnectError means a wrong host, port, or blocked connection, and SMTPNotSupportedError means STARTTLS isn't available on that port. Read the traceback first, then fix the matching layer.
"SMTP not working in Python" is not a specific error string — it's how people describe any failure when sending mail through Python's standard-library smtplib module. There is no single fix, because the underlying problem could be authentication, the connection, TLS negotiation, or the message itself. The fastest path to a solution is to read the exception in your traceback and fix the layer it points to.
smtplib raises a typed exception for each kind of failure (all subclasses of SMTPException, which is itself an OSError). The exception name tells you which layer broke:
SMTPAuthenticationError — What broke: Login; Typical cause: Wrong password, or provider rejects account password (e.g. Gmail)SMTPServerDisconnected — What broke: Connection dropped; Typical cause: Connected before TLS, idle timeout, or used the wrong portSMTPConnectError / TimeoutError — What broke: Couldn't connect; Typical cause: Wrong host/port, firewall blocking 587/465, no networkSMTPNotSupportedError — What broke: STARTTLS / AUTH unsupported; Typical cause: Called starttls() on a port that doesn't offer itSMTPSenderRefused — What broke: MAIL FROM rejected; Typical cause: From-address not authorized for the accountSMTPRecipientsRefused — What broke: RCPT TO rejected; Typical cause: Invalid recipient, or sender not allowed to relaySMTPDataError — What broke: Message body rejected; Typical cause: Content/policy rejection by the serverSource: Python smtplib exception reference.
If you see SMTPAuthenticationError, the server did not accept your username/password. The most common modern cause is Gmail / Google Workspace: since 30 May 2022 Google no longer accepts your normal account password over SMTP, and "Less Secure Apps" was removed. Do not look for a "less secure apps" toggle — it's gone.
Instead, enable 2-Step Verification on the account, then create an App Password and use that 16-character value in login():
import smtplibfrom email.message import EmailMessagemsg = EmailMessage()msg["From"] = "you@gmail.com"msg["To"] = "recipient@example.com"msg["Subject"] = "Test"msg.set_content("Hello from Python")with smtplib.SMTP("smtp.gmail.com", 587) as server:server.starttls() # upgrade to TLSserver.login("you@gmail.com", "abcd efgh ijkl mnop") # App Passwordserver.send_message(msg)
For other providers, use API-key-style SMTP auth instead of your dashboard login — but the exact convention differs per provider. SendGrid requires the literal username apikey plus your API key as the password. Mailgun uses a per-domain SMTP user (default postmaster@yourdomain.com, or a custom SMTP user you create) with its own SMTP password — not a literal apikey string. Amazon SES uses your IAM Access Key ID as the username with an SES-specific SMTP password derived from your AWS credentials (not your raw AWS secret key). Check your provider's SMTP credentials page rather than assuming one convention applies everywhere.
SMTPConnectError, SMTPServerDisconnected, or a TimeoutError mean Python never got a healthy session. Check, in order:
SMTP(host, 587) starttls() for explicit TLS (submission), or SMTP_SSL(host, 465) for implicit TLS. Per RFC 8314 both 587 and 465 are valid — 465 is not deprecated. Don't mix them: calling starttls() after SMTP_SSL (already encrypted) breaks, and connecting to 465 with plain SMTP() hangs.nc -vz smtp.gmail.com 587 or openssl s_client -connect smtp.gmail.com:465.sendmail() before login() on a server that requires auth gets you disconnected.SMTPNotSupportedError: STARTTLS extension not supported?This means you called starttls() but the server didn't advertise STARTTLS on that connection. Either you're on an implicit-TLS port (465) where TLS is already active — drop starttls() and use SMTP_SSL — or the server only offers STARTTLS after an EHLO. smtplib sends EHLO automatically, but if you call ehlo()/starttls() manually, send EHLO again after starttls() before login().
The "Python install is broken, reinstall Python" advice from older guides is almost never the real cause — smtplib ships with CPython and rarely fails to import. Catch the specific exception and act on its code:
try:with smtplib.SMTP_SSL("smtp.gmail.com", 465) as s:s.login(user, app_password)s.send_message(msg)except smtplib.SMTPAuthenticationError as e:print("Auth rejected:", e.smtp_code, e.smtp_error) # check App Passwordexcept smtplib.SMTPServerDisconnected:print("Dropped — check port/TLS pairing")except (smtplib.SMTPConnectError, TimeoutError):print("Can't reach host — check firewall/port")
If you're sending notifications at scale and don't want to babysit SMTP transports and provider auth quirks, an API like Courier abstracts the SMTP layer behind a single HTTP call.
References
FAQ
No. It's an umbrella description, not an SMTP reply code or a named exception. The actual failure is one of smtplib's typed exceptions — SMTPAuthenticationError, SMTPServerDisconnected, SMTPConnectError, or SMTPNotSupportedError. Read the traceback to see which one you got and fix that layer.
One API, every provider
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 5321 §3.3. Last reviewed Jul 1, 2026. Courier is not affiliated with third-party providers; error behavior may vary by implementation.
© 2026 Courier. All rights reserved.