SMTP

SMTP Not Working in Python

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.

What does "SMTP not working in Python" actually mean?

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 port
  • SMTPConnectError / TimeoutError — What broke: Couldn't connect; Typical cause: Wrong host/port, firewall blocking 587/465, no network
  • SMTPNotSupportedError — What broke: STARTTLS / AUTH unsupported; Typical cause: Called starttls() on a port that doesn't offer it
  • SMTPSenderRefused — What broke: MAIL FROM rejected; Typical cause: From-address not authorized for the account
  • SMTPRecipientsRefused — What broke: RCPT TO rejected; Typical cause: Invalid recipient, or sender not allowed to relay
  • SMTPDataError — What broke: Message body rejected; Typical cause: Content/policy rejection by the server

Source: Python smtplib exception reference.

How do I fix SMTP authentication failing in Python?

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 smtplib
from email.message import EmailMessage
msg = 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 TLS
server.login("you@gmail.com", "abcd efgh ijkl mnop") # App Password
server.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.

How do I fix connection / disconnect errors?

SMTPConnectError, SMTPServerDisconnected, or a TimeoutError mean Python never got a healthy session. Check, in order:

  1. Host and port match the transport. Use 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.
  2. The port is reachable. Corporate networks and cloud hosts often block outbound 25/587/465. Test with nc -vz smtp.gmail.com 587 or openssl s_client -connect smtp.gmail.com:465.
  3. You authenticate before sending. Calling sendmail() before login() on a server that requires auth gets you disconnected.

How do I fix 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().

Reading the error is the fix

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 Password
except 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.

FAQ

Common questions

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

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 5321 §3.3. Last reviewed Jul 1, 2026. Courier is not affiliated with third-party providers; error behavior may vary by implementation.