Resources / SMTP Errors
Bounces and DSN Parsing (RFC 3464, 3461)
A bounce is a machine-readable Delivery Status Notification, not a prose apology. This page reads the DSN format from RFC 3464, the SMTP request extension from RFC 3461, how to classify hard vs soft from the Action and Status fields, and where provider 421 4.7.x codes fit.
Last checked: June 22, 2026
When mail cannot be delivered, you get a bounce. A well-formed bounce is not a paragraph of apology text - it is a Delivery Status Notification (DSN): a structured, machine-readable message you can parse to learn exactly which recipient failed, why, and whether it is worth trying again. Treating bounces as parseable data instead of human prose is what makes automated, correct suppression possible.
This page reads the DSN format defined by RFC 3464 (An Extensible Message Format for Delivery Status Notifications, January 2003), the SMTP extension that lets a sender request DSNs from RFC 3461, and the multipart/report container they live in from RFC 3462. It builds on Reading SMTP replies (the basic codes) and Enhanced status codes (the X.Y.Z codes that appear in the Status field).
The 60-second version
- A DSN is a MIME message with top-level type
multipart/report; report-type=delivery-status(RFC 3464 §2). - It has up to three parts: a human-readable explanation, a machine-readable
message/delivery-status, and (optionally) the returned original message or its headers. - The machine-readable part has per-message fields (once) and per-recipient fields (one group per recipient).
- The two fields you must parse are
Action(failed/delayed/delivered/relayed/expanded) andStatus(an enhancedX.Y.Zcode). - Hard bounce =
Action: failedwith a5.x.xstatus. Soft bounce = a transient condition (Action: delayed, or a4.x.xstatus). Suppress hard, retry soft. - A bounce DSN is always sent with a null return path (
MAIL FROM:<>) so it cannot itself bounce and loop. - Senders can control bounces with the RFC 3461 parameters:
NOTIFY,ORCPT,RET,ENVID.
The container: multipart/report
A DSN rides inside the multipart/report content type (RFC 3462), the generic envelope for mail-system administrative reports. RFC 3464 §2 specifies how it is used for delivery status:
- The
report-typeparameter isdelivery-status. - The first body part is a human-readable explanation.
- The second body part is
message/delivery-status(the machine-readable data, §2.1). - The third body part, if present, is the original message or a portion of it (returned content).
The same multipart/report mechanism with report-type=feedback-report carries ARF complaint reports - a different report type for a different purpose (covered in Suppression and consent). The container is shared; the report-type parameter tells them apart.
Anatomy of a real DSN
Here is the multi-recipient example from RFC 3464 (Appendix E), lightly trimmed. It shows three recipients with three different outcomes in one report:
Content-Type: multipart/report; report-type=delivery-status;
boundary="JAA13167.773673707/CS.UTK.EDU"
--JAA13167.773673707/CS.UTK.EDU
content-type: text/plain; charset=us-ascii
----- The following addresses had delivery problems -----
<arathib@vnet.ibm.com> (unrecoverable error)
<wsnell@sdcc13.ucsd.edu> (unrecoverable error)
--JAA13167.773673707/CS.UTK.EDU
content-type: message/delivery-status
Reporting-MTA: dns; cs.utk.edu
Original-Recipient: rfc822;arathib@vnet.ibm.com
Final-Recipient: rfc822;arathib@vnet.ibm.com
Action: failed
Status: 5.0.0 (permanent failure)
Diagnostic-Code: smtp; 550 'arathib@vnet.IBM.COM' is not a
registered gateway user
Remote-MTA: dns; vnet.ibm.com
Original-Recipient: rfc822;johnh@hpnjld.njd.hp.com
Final-Recipient: rfc822;johnh@hpnjld.njd.hp.com
Action: delayed
Status: 4.0.0 (hpnjld.njd.jp.com: host name lookup failure)
Original-Recipient: rfc822;wsnell@sdcc13.ucsd.edu
Final-Recipient: rfc822;wsnell@sdcc13.ucsd.edu
Action: failed
Status: 5.0.0
Diagnostic-Code: smtp; 550 user unknown
Remote-MTA: dns; sdcc13.ucsd.edu
--JAA13167.773673707/CS.UTK.EDU
content-type: message/rfc822
[original message goes here]
--JAA13167.773673707/CS.UTK.EDU--
Notice the structure: one Reporting-MTA line (per-message), then a blank-line-separated group of fields per recipient. The first recipient is a hard failure, the second is merely delayed, the third is a hard “user unknown.” A naive parser that read only the first line of text would miss that two of three are permanent.
A second example from RFC 3464 (Appendix E, “Simple DSN”) shows the subtle case that trips up naive classifiers - a failed action with a transient 4.0.0 status, sent after the MTA gave up retrying for five days:
Content-Type: multipart/report; report-type=delivery-status;
boundary="RAA14128.773615765/CS.UTK.EDU"
--RAA14128.773615765/CS.UTK.EDU
----- The following addresses had delivery problems -----
<louisl@larry.slip.umd.edu> (unrecoverable error)
Message could not be delivered for 5 days
Message will be deleted from queue
--RAA14128.773615765/CS.UTK.EDU
content-type: message/delivery-status
Reporting-MTA: dns; cs.utk.edu
Original-Recipient: rfc822;louisl@larry.slip.umd.edu
Final-Recipient: rfc822;louisl@larry.slip.umd.edu
Action: failed
Status: 4.0.0
Diagnostic-Code: smtp; 426 connection timed out
Last-Attempt-Date: Thu, 7 Jul 1994 17:15:49 -0400
--RAA14128.773615765/CS.UTK.EDU
content-type: message/rfc822
[original message goes here]
--RAA14128.773615765/CS.UTK.EDU--
The Action: failed says the MTA has stopped trying; the Status: 4.0.0 says the underlying reason was transient (a connection timeout). The address is not provably dead - it never gave a 5.x.x. Suppressing it permanently on this one report would be wrong; this is the failed-but-transient case discussed below.
The machine-readable part: field by field
The body of message/delivery-status is “one or more ‘fields’ formatted according to the ABNF of RFC 822 header ‘fields’” (RFC 3464 §2.1): the per-message fields first, a blank line, then one or more groups of per-recipient fields each preceded by a blank line.
Per-message fields (RFC 3464 §2.2)
| Field | Required? | Meaning |
|---|---|---|
Reporting-MTA | Required | The MTA that attempted the delivery/relay/gateway described. Format mta-name-type; mta-name, normally dns; host.example.com. |
Original-Envelope-Id | Optional | The envelope ID from submission (the RFC 3461 ENVID), if one was supplied. Case-sensitive. Not the same as Message-Id. |
Received-From-MTA | Optional | The MTA the message was received from. |
Arrival-Date | Optional | When the message arrived at the Reporting MTA. |
DSN-Gateway | Conditional | Present only when the DSN was translated from a foreign (non-Internet) report. |
Per-recipient fields (RFC 3464 §2.3)
| Field | Required? | Meaning |
|---|---|---|
Final-Recipient | Required | The recipient address as the Reporting MTA had it. Format address-type; address, normally rfc822; user@example.com. |
Action | Required | What the MTA did: failed / delayed / delivered / relayed / expanded. |
Status | Required | The transport-independent enhanced status code, X.Y.Z. |
Original-Recipient | Optional | The address the sender originally specified (from the RFC 3461 ORCPT). Best key for matching back to your list. |
Remote-MTA | Optional | The “next hop” MTA that reported the failure. Its presence means the Diagnostic-Code came from the remote server, not the reporting one. |
Diagnostic-Code | Optional | The actual transport reply, e.g. smtp; 550 user unknown. The raw text behind the abstract Status. |
Last-Attempt-Date | Optional | When the last delivery attempt happened. |
Will-Retry-Until | Optional | For delayed DSNs only: when the MTA will give up. |
RFC 3464 §3 sets the minimum: a conforming report needs only Reporting-MTA plus, for each recipient, Final-Recipient, Action, and Status. So those four fields are the ones you can always rely on; everything else is “supply when available.”
Parsing Action and Status - the two fields that matter
The Action field (RFC 3464 §2.3.3)
action-value is one of exactly five keywords (case-insensitive):
| Action | Meaning (RFC 3464 §2.3.3) | Terminal? |
|---|---|---|
failed | ”The message could not be delivered… The Reporting MTA has abandoned any attempts.” | Yes - this is a bounce. |
delayed | ”Unable to deliver or relay the message, but it will continue to attempt to do so.” | No - more notifications may follow. |
delivered | ”Successfully delivered to the recipient address… does not indicate that the message has been read.” | Yes. |
relayed | ”Relayed or gatewayed into an environment that does not accept responsibility for generating DSNs.” | Yes (no further confirmation expected). |
expanded | ”Delivered to the recipient… and forwarded beyond that destination to multiple additional recipient addresses.” | No. |
The Status field (RFC 3464 §2.3.4)
The Status field carries an enhanced X.Y.Z code: “The first sub-field indicates whether the delivery attempt was successful (2 = success, 4 = persistent temporary failure, 5 = permanent failure).” Its detailed meaning is the enhanced status code set. The Status field is required precisely because it is transport-independent: “any DSN, regardless of origin, may be understood by any user agent or gateway that parses DSNs.”
Action and Status are not redundant
RFC 3464 §2.3.3 is explicit that you need both: “a ‘temporary failure’ (‘4’) status code could be used with an action-value of either ‘delayed’ or ‘failed’.” The example given: an MTA repeatedly times out on DNS, issues a delayed DSN after a few hours (Status: 4.x.x), then after a few days gives up and issues a failed DSN - with the same 4.x.x status. So a failed action with a 4.x.x status is a real, valid combination: the condition was transient, but the MTA exhausted its retry window. Classify on the combination, not on either field alone.
Hard bounce vs soft bounce
| Classification | Signals | What to do |
|---|---|---|
| Hard bounce | Action: failed with a 5.x.x Status (e.g. 5.1.1 user unknown, 5.1.2 bad domain) | Suppress the address. Do not send again. |
| Soft bounce | Action: delayed, or a 4.x.x Status | Retry on a backoff; do not suppress prematurely. |
| Failed-but-transient | Action: failed with a 4.x.x Status | The MTA gave up on a transient condition. Treat as a delivery failure for this send, but the address is not necessarily dead - apply consecutive-failure logic rather than immediate permanent suppression. |
| Success | Action: delivered / relayed / expanded, 2.x.x Status | Delivered to next hop. (Not the same as inbox placement.) |
The mapping back to the SMTP layer is consistent: the Status class digit is the same 2/4/5 verdict as the basic SMTP first digit, and the Diagnostic-Code field preserves the literal SMTP reply (smtp; 550 user unknown) when you want to see exactly what the remote server said.
RFC 3464 Appendix C adds a practical caution for list operators: “almost any failure status code (even a ‘permanent’ one) can result from a temporary condition,” so it recommends deleting a subscriber not on a single failure DSN “but only on the persistence of delivery failure over a period of time,” and that “subscribers should not be removed from a list due to ‘delayed’ reports.” Persistent user unknown over several days, however, “could be considered a reliable indication that address [is] no longer valid.”
Bounce-handling best practice
Parsing a DSN correctly is only half the job; what you do with the classification is what protects deliverability. The consolidated guidance from the standards and from M3AAWG’s Sender BCP:
- Suppress confirmed hard bounces immediately. A
failedaction with a5.x.xstatus (especially5.1.1/5.1.2) is a dead or refused address; mail it again and you manufacture the exact signal receivers penalize. M3AAWG: receivers “penalize a sending IP for too many messages that end up bouncing with permanent failures,” and “large volumes of hard-bounces are too often indicative of a poorly managed registration process.” - Never suppress on a single soft bounce. Retry
4.x.xanddelayedon a backoff (the RFC 5321 §4.5.4 floor: delay between attempts, give up after several days). - Suppress persistent soft bounces by threshold, not by one event. M3AAWG’s rule of thumb: remove an address that “bounces consecutively at least two times over two weeks or more.” RFC 3464 Appendix C agrees in principle - remove “only on the persistence of delivery failure over a period of time.”
- Match the report to the right recipient. Key on
Original-Recipient/ENVIDso forwarding does not cause you to suppress the wrong address (or fail to suppress the right one). - Feed the result into one suppression set. Hard bounces flow into the same suppression list as complaints and unsubscribes, enforced before every send.
Two things a bounce does not tell you. It does not distinguish a self-inflicted reputation block from a real dead address unless you read the code: a 5.7.x policy/authentication rejection points at reputation or authentication, not the recipient, and suppressing the address would hide the real problem. And Action: delivered is acceptance at the next hop, never proof of inbox placement.
Why a bounce never bounces: the null return path
A DSN “MUST be addressed… to the return address from the transport envelope which accompanied the original message” - i.e. the original MAIL FROM. And when the DSN itself is sent, “the MAIL FROM command MUST use a NULL return address, i.e., MAIL FROM:<>” (RFC 3464 §2). RFC 3461 §5.2 reinforces it: “A DSN MUST NOT be returned to the sender for any message for which the return address from the SMTP MAIL command was NULL (<>).” This is the loop-prevention rule - a bounce sent from <> cannot generate a bounce of its own. If you operate a return path (a bounce mailbox), it must accept mail addressed from <>.
Controlling bounces at send time: the RFC 3461 extension
RFC 3464 defines the format of a DSN; RFC 3461 defines how a sender requests one over SMTP. A server advertises support with the DSN keyword in its EHLO response. Then four parameters become available:
| Parameter | Command | Purpose (RFC 3461 §4) |
|---|---|---|
NOTIFY | RCPT | When to generate a DSN: NEVER, or a comma-list of SUCCESS / FAILURE / DELAY. NEVER must appear alone. |
ORCPT | RCPT | The “original” sender-specified recipient address, preserved through forwarding (returned in Original-Recipient). |
RET | MAIL | Whether a failure DSN returns the FULL message or only HDRS (headers). |
ENVID | MAIL | An envelope identifier echoed back in Original-Envelope-Id, so you can correlate the DSN to the exact transaction. |
A real submission with these parameters (RFC 3461 §10.1):
C: EHLO Example.ORG
S: 250-Example.ORG
S: 250-DSN
S: 250 SIZE
C: MAIL FROM:<Alice@Example.ORG> RET=HDRS ENVID=QQ314159
S: 250 <Alice@Example.ORG> sender ok
C: RCPT TO:<Bob@Example.COM> NOTIFY=SUCCESS ORCPT=rfc822;Bob@Example.COM
S: 250 <Bob@Example.COM> recipient ok
C: RCPT TO:<Carol@Ivory.EDU> NOTIFY=FAILURE ORCPT=rfc822;Carol@Ivory.EDU
S: 250 <Carol@Ivory.EDU> recipient ok
Defaults and limits worth knowing:
- Absent a
NOTIFYparameter, behavior “may be interpreted as either NOTIFY=FAILURE or NOTIFY=FAILURE,DELAY” (§4.1) - failure notifications are the default. - A conforming server “MUST NOT refuse a MAIL command based on the absence or presence” of
ENVID/RET, nor aRCPTbased onNOTIFY/ORCPT(§5.1). These parameters never cause a rejection by themselves; an invalid value yields501. - When a DSN is itself transmitted, “the RET parameter MUST NOT be used. The NOTIFY parameter MAY be used, but its value MUST be NEVER” (§6.1) - another loop guard.
ENVIDis capped at 100 characters,ORCPTat 500,NOTIFYat 28,RETat 8 (§5.4).
ORCPT is the most useful field for senders: because it survives forwarding unchanged, the Original-Recipient in the returned DSN matches the address you put on your list, which is the reliable key for automatic suppression even when the mail was forwarded and the Final-Recipient was rewritten.
Provider-proprietary 421 4.7.x patterns (not base RFC)
In production you will see compound codes like 421 4.7.0 or 421 4.7.x from large providers (for example Gmail and Microsoft) during throttling, reputation blocks, or policy rejections. It is important to be precise about what these are:
421is the basic SMTP reply for “service not available, closing transmission channel” (RFC 5321) - a transient4xx.4.7.xis the Security or Policy enhanced class (X.7.x) from RFC 3463, with class4(persistent transient).
The two frameworks are real and standard. But the specific compound code 421 4.7.x is not defined as a single code point in any base RFC. Providers emit these by combining the RFC 5321 basic code with an RFC 3463-shaped enhanced code. Some X.7.x detail numbers are registered - RFC 3463 defined X.7.0-X.7.7, RFC 5248 added X.7.10/X.7.13/X.7.14, and RFC 7372 registered the authentication-failure codes X.7.20 through X.7.26 (no passing DKIM signature, SPF validation failed/error, reverse-DNS failure, multiple auth failures; see Enhanced status codes). Detail numbers outside the IANA registry, however, are vendor-defined extensions, not standardized meanings. Treat an unregistered code by its shape: a 421 4.7.x is a transient policy/security deferral, so retry on a backoff and investigate reputation/authentication - but do not attribute a precise meaning for an unregistered detail number to a base RFC. Look up the specific provider’s documentation, and confirm registered codes against the IANA enhanced-status-code registry rather than guessing.
A worked distinction: a 550 5.7.23 is registered - RFC 7372 defines it as “SPF validation failed,” so you know to fix SPF alignment, not the recipient. A 421 4.7.28 from a large provider is not in the registry; its 4.7.x shape tells you it is a transient policy/reputation deferral, but the exact .28 meaning lives in that provider’s own postmaster documentation, and you should not cite RFC 3463 for it.
Common confusion
- A bounce is data, not prose. The human-readable first part is for people; parse the
message/delivery-statuspart for decisions. Action: failedwithStatus: 4.x.xis valid - the MTA gave up on a transient problem. Do not auto-classify it as a permanent dead address.deliveredis not “read,” and is not “inbox.” It means placed in the next-hop mailbox/system, nothing about engagement or folder.- Match on
Original-Recipientwhen present, notFinal-Recipient, because forwarding rewrites the final address. 421 4.7.xis transient and provider-specific - retry, investigate reputation; don’t permanently suppress on it and don’t cite a base RFC for the detail code.
What this means for you, and what Egressif does
Egressif parses bounces as DSNs, not text. We read the message/delivery-status part, key suppression decisions on the Action and Status fields, and use Original-Recipient/ENVID to tie each report back to the exact recipient and send. A failed action with a 5.x.x status is suppressed immediately (a confirmed dead or refused address); a delayed action or 4.x.x status is retried on a backoff, with persistent failure over time - not a single report - triggering suppression, exactly as RFC 3464 Appendix C advises. Provider 421 4.7.x deferrals are handled as transient policy signals that feed reputation monitoring rather than the suppression list. Our return path accepts the null-sender (<>) bounces the standard requires, so notifications arrive instead of silently looping. What happens to a suppressed address afterward - and how complaints feed the same list - is covered in Suppression and consent.
Related references
- Reading SMTP Replies (RFC 5321) An SMTP server answers every command with a three-digit reply code. The first digit tells you whether it succeeded, failed temporarily, or failed permanently. This page reads those codes the way RFC 5321 defines them, with the full numeric table and real transcripts.
- Enhanced Status Codes (RFC 3463, 5248, 7372) Enhanced status codes are the X.Y.Z numbers that ride alongside a basic SMTP reply and say precisely what went wrong. This page explains the class.subject.detail structure from RFC 3463, the IANA registry defined by RFC 5248, the authentication codes added by RFC 7372, and the specific codes worth recognizing.
Tell us what you run today.
Domains, rough volume, current providers, and what hurts. You will get a straight answer on fit, and a real number, in one conversation.