egressif.

Resources / SMTP Errors

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.

Last checked: June 22, 2026

When you send mail, the receiving server answers every command you issue with a three-digit reply code followed by text. That code is the entire conversation: it tells your MTA whether the command worked, whether to try again later, or whether to stop. Reading it correctly is the difference between a queue that drains and a queue that hammers a server that already told you “no.”

This page reads SMTP replies exactly as RFC 5321 (the Simple Mail Transfer Protocol, October 2008) defines them. Everything here is base protocol; the human-readable enhanced codes layered on top (the X.Y.Z numbers) are covered separately in Enhanced status codes.

CLIENT SENDSSERVER REPLIESEHLO mail.example.com250 - greeting, readyMAIL FROM: empty250 - sender acceptedRCPT TO: user@dest250 - recipient acceptedDATA354 - start mail inputmessage then end marker250 - queued, accepted2xx accepted4xx transient - retry later5xx permanent - stop
The server answers every command with a three-digit code; the first digit is the verdict - 2xx accepted, 4xx retry later, 5xx stop.

The 60-second version

  • Every reply is three digits. The first digit is the verdict: 2 good, 3 send more, 4 temporary failure, 5 permanent failure.
  • 4xx is transient. The command “can be successful if repeated without any change.” Your client SHOULD try again later.
  • 5xx is permanent. The client SHOULD NOT repeat the same request in the same sequence. Retrying the identical thing is wasted effort and a reputation signal.
  • 421 is the odd one out: it is a 4xx (transient), but it means “service not available, closing the channel” and can arrive in response to any command.
  • After you send the message body and the final <CRLF>.<CRLF>, the reply to that is the one that decides delivery. A 2yz there means the server has accepted responsibility for the message.
  • The second digit is a category (syntax, information, connections, mail system); useful context, but the first digit drives behavior.

Anatomy of a reply code

RFC 5321 §4.2.1 is explicit: “The three digits of the reply each have a special significance. The first digit denotes whether the response is good, bad, or incomplete.”

First digit - the severity class

ClassNameWhat it means for you
2yzPositive Completion”The requested action has been successfully completed. A new request may be initiated.”
3yzPositive Intermediate”The command has been accepted, but the requested action is being held in abeyance, pending receipt of further information.” Send the next thing (this is what 354 after DATA is).
4yzTransient Negative Completion”The command was not accepted, and the requested action did not occur. However, the error condition is temporary… the SMTP client SHOULD try again.”
5yzPermanent Negative Completion”The command was not accepted and the requested action did not occur. The SMTP client SHOULD NOT repeat the exact request (in the same sequence).”

The spec gives a rule of thumb for the 4yz/5yz boundary that is worth memorizing: “replies are 4yz if they can be successful if repeated without any change in command form or in properties of the sender or receiver.” A 5yz needs something to change first - the address, the content, the account status, your IP’s reputation - before it is worth trying again.

RFC 5321 also concedes that “transient” is genuinely hard to pin down “when two different sites… must agree on the interpretation,” and that “each reply in this category might have a different time value.” That is why retry strategy is a SHOULD, not a fixed schedule (see below).

Second digit - the category

Second digitCategory (RFC 5321 §4.2.1)
x0zSyntax errors, commands that fit no functional category, unimplemented/superfluous commands
x1zInformation (replies to status or help requests)
x2zConnections (the transmission channel)
x3zUnspecified
x4zUnspecified
x5zMail system (status of the receiver’s mail system regarding the requested transfer)

The second digit is informational. Do not branch your retry logic on it - branch on the first digit, and on the enhanced X.Y.Z code when one is present.

Third digit - the fine gradation

RFC 5321 §4.2.1 says the third digit “gives a finer gradation of meaning in each of the categories specified by the second digit.” In the basic protocol it is not independently meaningful the way an enhanced X.Y.Z detail code is - the spec assigns specific three-digit values (250, 421, 550, and so on, listed in full below) rather than a general algebra of the third digit. For precise reasons, the human-readable signal is the enhanced status code that modern servers append (see Enhanced status codes); the basic code’s job is the verdict, not the diagnosis.

The greeting and the EHLO handshake

Before any mail moves, two exchanges set up the session, and both are answered with reply codes you must read.

The greeting. The moment the TCP connection opens, the server speaks first with a 220 (“Service ready”) or, if it is refusing the connection outright, a 554 (“No SMTP service here”) or a 421 (“Service not available”). RFC 5321 §4.3.2 lists 220 as the only success here; you do not send anything until you have seen it.

EHLO and capability discovery. The client then issues EHLO <its own domain>. A 250 multiline reply lists the extensions the server supports - SIZE, STARTTLS, 8BITMIME, DSN, PIPELINING, AUTH, and so on. This is how a sender learns, for example, that it may request delivery-status notifications (the DSN keyword; see Bounces and DSN parsing) or must negotiate TLS first. If the server does not understand EHLO it answers 500/502, and the client falls back to the older HELO, which gets a single-line 250 and offers no extension list. The errors RFC 5321 allows here are 504, 550, and 502.

S: 220 mx.example.com ESMTP Postfix
C: EHLO mail.sender.example
S: 250-mx.example.com
S: 250-PIPELINING
S: 250-SIZE 52428800
S: 250-STARTTLS
S: 250-8BITMIME
S: 250-DSN
S: 250 SMTPUTF8

Reading the EHLO reply correctly is what lets the rest of the transaction be well-formed: it tells you the size cap (so you do not waste a transaction on an oversized message), whether you can upgrade to TLS, and whether you can ask for DSNs.

Multiline replies

A reply can span several lines. Each line repeats the same code; on every line except the last the code is followed by a hyphen (-), and the last line uses a space. That is how a client knows the reply is finished. An EHLO response is the canonical example, because the server lists each extension on its own line:

S: 250-mail.example.com
S: 250-DSN
S: 250-SIZE 52428800
S: 250 STARTTLS

The first three lines carry 250-; the final line carries 250 (space) to close the block. Permanent failures use the same mechanism:

S: 550-mailbox unavailable
S: 550 user has moved with no forwarding address

Parse the code from each line and only treat the reply as complete when you see the space form. Joining the text lines gives you the full human-readable explanation.

The command sequence and its replies

A normal SMTP transaction is a fixed dance. RFC 5321 §4.3.2 lists the success (S) and error (E) codes allowed at each step:

StepSuccessErrors
Connection established (greeting)220554
EHLO / HELO250504, 550, 502
MAIL (envelope sender)250552, 451, 452, 550, 553, 503, 455, 555
RCPT (envelope recipient)250, 251550, 551, 552, 553, 450, 451, 452, 503, 455, 555
DATA354 then data, then 250552, 554, 451, 452; 450, 550 for policy rejections; 503, 554 before data
RSET250

And 421 (“Service shutting down and closing transmission channel”) “can be returned in response to any command” (§4.3.2) - including mid-transaction, if the server decides it must stop.

A clean delivery looks like this:

S: 220 mx.example.com ESMTP ready
C: EHLO mail.sender.example
S: 250-mx.example.com
S: 250 SIZE 52428800
C: MAIL FROM:<news@sender.example>
S: 250 2.1.0 Sender OK
C: RCPT TO:<user@example.com>
S: 250 2.1.5 Recipient OK
C: DATA
S: 354 End data with <CR><LF>.<CR><LF>
C: (message headers and body)
C: .
S: 250 2.0.0 OK: queued as 4F1a2b3c
C: QUIT
S: 221 2.0.0 Bye

A recipient that does not exist is rejected right at RCPT, and the rest of the transaction never happens for that address:

C: RCPT TO:<nobody@example.com>
S: 550 5.1.1 <nobody@example.com>: Recipient address rejected: User unknown

A temporary deferral (greylisting, rate control, a busy mailbox) comes back as 4xx and is yours to retry:

C: RCPT TO:<user@example.com>
S: 450 4.2.1 <user@example.com>: Recipient address rejected: greylisted, try again later

A server can also reject after you have transmitted the whole message - the reply to the final dot is a 5xx, and the body you just sent is refused for content or policy reasons:

C: DATA
S: 354 End data with <CR><LF>.<CR><LF>
C: (message headers and body)
C: .
S: 550 5.7.1 Message rejected as spam by content filter
C: QUIT
S: 221 2.0.0 Bye

And a 421 can arrive mid-transaction if the server decides it must stop - here it tears the connection down while you are still adding recipients:

C: MAIL FROM:<news@sender.example>
S: 250 2.1.0 Ok
C: RCPT TO:<a@example.com>
S: 250 2.1.5 Ok
C: RCPT TO:<b@example.com>
S: 421 4.7.0 mx.example.com Error: too many connections from your IP, try later

The 421 is transient: requeue the message and reconnect later (typically from fewer parallel connections), do not treat it as a permanent rejection of the recipients.

The reply that actually decides delivery: after <CRLF>.<CRLF>

The most consequential reply in the whole transaction is the one to the end-of-data terminator. RFC 5321 §4.2.5 spells out who owns the message after each outcome:

  • 2yz after the final dot - the server “accepts responsibility for delivering the message” (or, on transient downstream failure, retrying, and on permanent failure, returning a notification to the sender). Once you get a 250 here, the message is the receiver’s problem. Do not resend it.
  • 4yz after the final dot - the server “MUST NOT make a subsequent attempt to deliver that message.” Responsibility stays with you: requeue it or return it.
  • 5yz after the final dot - the server “MUST NOT make any subsequent attempt to deliver the message,” and you “SHOULD NOT again attempt delivery to the same server without user review… and appropriate intervention.”

This is why a 250 to the dot is “delivered to the next hop,” not “landed in the inbox.” It is an acceptance of custody, nothing more about placement.

4xx vs 5xx: transient and permanent, in practice

Code rangeClassificationBehaviorCommon codes
2xxAccepted / successDelivered to next hop; do not resend250, 251, 252
4xxSoft failure / transient deferralSHOULD retry later421, 450, 451, 452
5xxHard failure / permanentSHOULD NOT retry the same request550, 551, 552, 553, 554

How long to keep retrying a 4xx

RFC 5321 §4.5.4 gives recommendations, not hard rules:

  • “The sender MUST delay retrying a particular destination after one attempt has failed.”
  • “In general, the retry interval SHOULD be at least 30 minutes.”
  • “Retries continue until the message is transmitted or the sender gives up; the give-up time generally needs to be at least 4-5 days.”
  • Experience favors “two connection attempts in the first hour… and then backing off to one every two or three hours.”

These are SHOULD-level; real MTAs tune them per destination and per reason. The principle is fixed though: a 4xx earns more attempts, a 5xx does not.

The full numeric reply table

From RFC 5321 §4.2.2-4.2.3, in numeric order:

CodeMeaning (RFC 5321)
211System status, or system help reply
214Help message (how to use the receiver or a non-standard command)
220<domain> Service ready
221<domain> Service closing transmission channel
250Requested mail action okay, completed
251User not local; will forward to <forward-path>
252Cannot VRFY user, but will accept message and attempt delivery
354Start mail input; end with <CRLF>.<CRLF>
421<domain> Service not available, closing transmission channel
450Requested mail action not taken: mailbox unavailable (busy or temporarily blocked)
451Requested action aborted: local error in processing
452Requested action not taken: insufficient system storage
455Server unable to accommodate parameters
500Syntax error, command unrecognized (may include “line too long”)
501Syntax error in parameters or arguments
502Command not implemented
503Bad sequence of commands
504Command parameter not implemented
550Requested action not taken: mailbox unavailable (not found, no access, or policy rejection)
551User not local; please try <forward-path>
552Requested mail action aborted: exceeded storage allocation
553Requested action not taken: mailbox name not allowed (syntax incorrect)
554Transaction failed (or, on connection open, “No SMTP service here”)
555MAIL FROM/RCPT TO parameters not recognized or not implemented

The 5xx syntax and sequence codes

The 500-504 band is different from the 55x mailbox-rejection band: it reports problems with the protocol, not the recipient. 500 (command unrecognized) and 501 (bad parameters/arguments) mean the server could not parse what you sent; 502 (command not implemented) and 504 (command parameter not implemented) mean it understood but does not support the command or option; 503 (bad sequence of commands) means you issued a command out of order - for example RCPT before MAIL, or DATA with no recipients accepted. In normal sending these should never appear; when they do, the bug is in the client or a middlebox, not the address, so suppression is the wrong response - fix the conversation.

A gotcha worth knowing: 552 vs 452 on “too many recipients”

There is one place where the obvious code is the wrong one. RFC 5321 §4.5.3.1 notes that the traditional reply for “too many recipients” was 552, but states plainly: “The correct reply code for this condition is 452. Clients SHOULD treat a 552 code in this context as if it were a 452 code.” So a 552 seen while adding recipients should be handled as a transient condition (back off, send fewer per transaction), not as a permanent rejection.

What did NOT change, and common confusion

  • A 250 is not “inbox.” It is acceptance of responsibility for the next hop. Placement is decided later by reputation, content, and engagement - the reply code says nothing about it.
  • The first digit, not the text, is authoritative. Servers write whatever prose they like after the code; branch your logic on the digits.
  • 4xx means try again unchanged; 5xx means change something first. Resending an identical message to a 5xx is the classic way to manufacture a reputation problem.
  • 421 is transient even though it closes the connection. Requeue and reconnect later; do not treat it as a permanent block.

What this means for you, and what Egressif does

Honoring reply codes correctly is unglamorous and it is most of deliverability hygiene. Egressif treats a 5xx (and the matching 5.x.x enhanced code) as permanent: the address is suppressed and not retried, because re-hitting a hard rejection is exactly the behavior receivers penalize and that erodes sender reputation - see Bounces and DSN parsing for how we classify them and Suppression and consent for what happens next. A policy rejection such as 550 5.7.1 often points not at the recipient but at authentication or reputation - if it cites an SPF/DKIM/DMARC failure, the fix is in DMARC and alignment, not the address. A 4xx is requeued and retried on a backoff that respects the RFC 5321 §4.5.4 floor (delay between attempts, give up after several days) and the destination’s signals, with 421 handled as a connection-level “come back later.” We do not resend a message a server has already accepted with a 250, and we read the reply to the end-of-data dot as the authoritative custody handoff it is - not as a promise of placement, which no reply code can make.

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.

Talk to our team