SMTPLIB
smtplib.SMTPRecipientsRefused with Relay access denied means the server won't relay for an unauthenticated session. Call starttls() and login() first.
Updated Jul 1, 2026
The short answer
Python's smtplib raises SMTPRecipientsRefused when the SMTP server refuses every recipient. The "Relay access denied" text (usually code 554 or 550, enhanced status 5.7.1) means the server won't forward mail to an external domain because your session isn't authorized. The fix is almost always to authenticate: call starttls() then login() with valid credentials before sendmail(), and send through a host that permits you to relay.
This error is the combination of two things: a Python smtplib exception and an SMTP server rejection.
smtplib.SMTPRecipientsRefused is raised by SMTP.sendmail() / SMTP.send_message() only when every recipient was refused ("All recipients were refused. Nobody got the mail." — Python docs). Its .recipients attribute is a dict mapping each refused address to a (code, message) tuple. If some recipients are accepted, no exception is raised and sendmail() returns that dict instead.X.7.1 means "the sender is not authorized to send to the destination," and the leading 5 (per RFC 5321 §4.2.1) marks it a permanent failure — retrying the same message unchanged will not help."Relaying" means asking a mail server to forward a message to a domain it isn't responsible for. Servers (Postfix's reject_unauth_destination is the classic source of this exact string) only relay for clients they trust — typically authenticated clients or clients in a trusted IP range. If you connect anonymously and try to send to an external recipient, the server refuses the RCPT TO and you get this exception.
In order of likelihood:
1. Authenticate before sending (the usual fix). Most code that hits this simply never logged in. You must call login() (over a secured connection) before sendmail():
import smtplibfrom email.message import EmailMessagemsg = EmailMessage()msg["From"] = "you@yourdomain.com"msg["To"] = "recipient@example.com"msg["Subject"] = "Test"msg.set_content("Hello")# Port 587 + STARTTLSwith smtplib.SMTP("smtp.yourprovider.com", 587) as server:server.starttls()server.login("you@yourdomain.com", "APP_OR_SMTP_PASSWORD")server.send_message(msg)# Or port 465 + implicit TLS (use SMTP_SSL)# with smtplib.SMTP_SSL("smtp.yourprovider.com", 465) as server:# server.login(...); server.send_message(msg)
Ports 587 (STARTTLS) and 465 (implicit TLS) are both valid (RFC 8314); pick the one your provider documents.
2. Use the right credentials. Wrong username/password produces SMTPAuthenticationError, not this error — but credentials that authenticate to a host that still won't relay for your From/recipient combination can yield "Relay access denied." Confirm you're using the provider's SMTP credentials, not your app's API key as a password where a token is expected.
3. For Gmail / Google Workspace: Less Secure Apps access was disabled for personal Gmail accounts on May 30, 2022; Google Workspace kept a phased deprecation that wasn't fully complete until May 2025. Either way, LSA is not available today — use an App Password (requires 2-Step Verification enabled) or OAuth2 as the SMTP password against smtp.gmail.com.
4. Send through a host that permits relaying for you. If you're connecting to the recipient's MX server (or a server where you have no account), it will never relay for you by design — point smtplib at your own provider's submission host instead.
5. If you run the server (Postfix): ensure authenticated submission is allowed, e.g. smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, and that the client connects on the submission port with SASL auth (Postfix: SMTPD_ACCESS_README).
Inspect the real cause: enable the protocol dialog with server.set_debuglevel(1), or catch the exception and read err.recipients to see the exact per-recipient code and text:
try:server.send_message(msg)except smtplib.SMTPRecipientsRefused as err:for addr, (code, text) in err.recipients.items():print(addr, code, text)
SMTPRecipientsRefused fires at RCPT TO (the recipient/relay step). SMTPSenderRefused fires earlier, at MAIL FROM (the server rejected your envelope sender). SMTPAuthenticationError fires at the login() step (bad credentials). "Relay access denied" specifically means the server accepted your sender but won't carry the message to that recipient — an authorization gap, not a bad address.
References
FAQ
sendmail() returns a dict of refused recipients only when at least one recipient was accepted. When every recipient is refused, smtplib raises SMTPRecipientsRefused instead, with the same dict on its .recipients attribute. So this exception specifically means nobody received the message.
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 3463 §3.8 (X.7.1); RFC 5321 §4.2.1. Last reviewed Jul 1, 2026. Courier is not affiliated with third-party providers; error behavior may vary by implementation.
© 2026 Courier. All rights reserved.