邮箱错误码解析指南:把 SMTP、Enhanced Status 和 DSN 变成结构化事件
面向开发者的邮件错误码解析方案:如何从 bounce 中抽取 SMTP 回复、X.Y.Z、DSN 字段,并驱动规则引擎、重试和告警。
从工程实现角度看,邮箱错误码不是“展示给人看”的文本,而是一套可解析、可归类、可回放的失败协议。你真正要存的不是一整段 bounce,而是 SMTP reply、enhanced status code、DSN 字段、对端域名、重试次数、发件身份和策略上下文。否则你永远只能在工单里说“邮件发不出去”。
RFC 5321 的三位数回复适合做粗分类;RFC 3463 的 X.Y.Z 适合做机器判断;RFC 3464 的 DSN 适合关联原始消息和投递结果。IANA 的 registry 则告诉你 enhanced code 是有注册语义的,不是随便猜的。
建议的解析链路
raw bounce text -> SMTP reply parser -> enhanced status extractor -> DSN field parser -> rule engine -> retry / fail / escalate
一个常见误区是只解析第一行。实际生产里,对端经常同时给出一行标准码、一行人类可读解释、若干 DSN 头部,甚至把有用信息放进 MIME part。解析器应该尽量保留原文,同时抽取结构化字段。
| 字段 | 来源 | 用途 |
|---|---|---|
smtp_reply | SMTP 对话 | 区分 4xx / 5xx |
enhanced_status | RFC 3463 | 细分原因 |
final_recipient | RFC 3464 | 确认最终目标地址 |
action | RFC 3464 | failed / delayed / delivered / relayed |
diagnostic_code | RFC 3464 | 保存对端原始诊断文本 |
remote_mta | RFC 3464 | 定位问题对端 |
reporting_mta | RFC 3464 | 定位本方报告来源 |
推荐的代码抽取规则
import re
SMTP_RE = re.compile(r'([245]\d\d)(?:[ -](\d\.\d+\.\d+))?')
DSN_RE = re.compile(r'([245]\d\.\d+\.\d+)')
# 例:550 5.1.1 user unknown
# 例:421 4.4.1 no answer from host
抽取时不要假定一定有 enhanced code。有些旧系统只返回 550 + 文本;有些网关会在同一条文本里放多个代码。建议优先抓最靠近 SMTP reply 的那个。
规则引擎怎么分层
- 4xx:标记为 transient_failure,加入延迟队列。
- 5.1.1 / 5.1.2 / 5.1.3:地址类错误,立刻失败。
- 5.2.2:邮箱满或存储不足,失败并提示重试窗口。
- 5.3.x / 5.4.x:系统、网络、路由问题,视对端性质决定是否重试。
- 5.7.1 / 5.7.8:认证、授权或策略拒绝,通常需要人工排查。
不要把 retry 逻辑写成“任何失败都重试三次”。邮件系统对反复重试非常敏感,尤其是策略拒绝和认证失败。对这些码值,应该先修配置再重新投递。
和 SPF / DKIM / DMARC 的关系
很多投递失败不是传输链路坏了,而是认证策略不通过。SPF / DKIM / DMARC 失败未必都以同一错误码出现;有的接收方会直接退 550 5.7.1,有的会给自定义文本,还有的会放进 Diagnostic-Code 里。因此解析器最好把邮件头里的认证结果也一起存下来。
auth_results = {
"spf": "pass",
"dkim": "fail",
"dmarc": "fail"
}
如果转发链路参与其中,SPF 可能因为发送 IP 改变而失效,DKIM 可能因为修改正文而失效,DMARC 可能因为对齐不一致而被判拒绝。程序上最稳妥的做法,是把这些结果和退信码一起聚合分析。
最小可用的事件模型
{{
"message_id": "abc123",
"recipient": "user@example.com",
"smtp_reply": "550 5.7.1",
"enhanced_status": "5.7.1",
"action": "failed",
"diagnostic_code": "smtp; 550 5.7.1 delivery not authorized",
"remote_mta": "mx.example.net",
"attempt": 1,
"retryable": false
}}
官方来源
- RFC 5321
- RFC 3463
- RFC 3464
- IANA SMTP enhanced status codes registry
- Google Workspace SMTP relay service error messages
- Google Workspace SMTP errors and codes
- Microsoft Exchange Online NDR documentation
实现层面的关键不是“识别多少个码”,而是把错误码变成稳定的事件模型,让队列、报警、重试和工单都能消费同一份事实。