You need to enable JavaScript to run this app. Python使用smtplib连接Office365发邮件报SMTPRecipientsRefused 421错误解决
优惠活动
大模型
产品
解决方案
定价
更多

Python使用smtplib连接Office365发邮件报SMTPRecipientsRefused 421错误解决

报错根源定位

这个报错是Office365 SMTP服务端的主动拦截:当单条连接内累计出现6次以上协议合规校验失败时,服务端会直接关闭传输通道,和smtplib本身的功能限制无关。对应你代码和业务场景的核心触发点:

  • 收件人格式不符合SMTP协议规范
    你直接把分号分隔的8个邮箱写入msg['To']字段,但SMTP协议要求RCPT TO指令的收件人分隔符必须是逗号,不支持分号。Office365网关会对每个识别到的收件人做格式校验,分号分隔的长串内容会被判定为无效地址,每出现一个无效地址就累计1次协议错误,8个地址的错误计数刚好突破6次的断连阈值。你测试3个收件人时流程正常,本质是错误计数没达到触发断连的阈值,不是收件人数量本身的问题。
  • 高频重复建立SMTP连接
    你在循环内部每发1封邮件就完成一次建连、TLS握手、登录、断开全流程,70组记录就要发起70次重复认证连接。短时间内的高频重复建连行为会被Office365的反滥用规则判定为异常流量,直接累计为协议错误计数。
  • 发件人字段与登录账号不匹配
    你将表格中读取的tosendfrom地址赋值给msg['From'],但实际SMTP认证用的是固定的xxx@xxx.com账号。Office365默认禁止无授权的发件人地址伪造,一旦From字段和认证账号不一致,就会触发协议合规校验失败,累计错误计数。
针对性修复方案
  • 统一清洗收件人格式
    拿到原始的分号分隔收件人字符串后,做标准化处理:将分号替换为SMTP要求的逗号分隔格式,拆分后逐个去掉前后空格、过滤空字符串,再组装为合法的收件人列表赋值给邮件头,示例代码:
    raw_recipients = Emaitstosendto
    # 分号转逗号,拆分清洗
    recipient_list = [addr.strip() for addr in raw_recipients.replace(";", ",").split(",") if addr.strip()]
    email_to = ",".join(recipient_list)
    
  • 修正发件人配置
    将msg['From']的取值改为与SMTP登录完全一致的认证账号,不要随意替换为表格内的其他发件人地址。如果需要留存不同的回复目标,可以通过设置Reply-To字段实现,避免直接修改From字段触发拦截。
  • 单次连接批量复用发送
    将SMTP建连、TLS初始化、登录的逻辑移到循环外部,整个批量发送流程只初始化一次连接,发完所有邮件再统一断开,避免每发一封就重复建连触发的异常计数。调整后的核心结构:
    # 循环外完成一次连接初始化
    with smtplib.SMTP(smtp_ssl_host, smtp_ssl_port) as s2:
        s2.starttls(context=context)
        s2.login(email_login, email_pass)
        # 遍历记录的逻辑全部移到连接块内部
        for i in range(len(emails_to)):
            # 原有邮件组装逻辑
            s2.send_message(msg)
    # 连接块退出时自动安全断开
    
  • 适配发送频率限制
    每发送10封邮件增加1-2秒的延时,匹配Office365单连接每分钟最多30封邮件的频率限制,避免触发421限流。

内容的提问来源于stack exchange,提问作者Herdil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.22 16:27:06