SMTP is a conversation in which the server answers every command with a numbered reply. A three-digit reply code carries the machine-readable verdict, an optional enhanced status code in x.y.z form narrows down the reason, and the human text at the end is free-form. Reading those numbers is the difference between "email is broken" and "the server rejected the password at AUTH with 535, so fix the credential."
Anatomy of an SMTP Reply
Every reply follows the same shape. The first token is always the three-digit code defined in RFC 5321; anything after it is optional detail for humans:
250 2.1.5 Recipient OK
│ │ │
│ │ └─ Free-form text — varies by server, never parse it for logic
│ └─────── Enhanced status code (RFC 3463), optional
└─────────── Reply code (RFC 5321), always present, always three digitsA reply can also span several lines. The server marks continuation with a hyphen after the code and the final line with a space. This is how the EHLO capability list arrives, and it is the single most misread piece of an SMTP transcript:
250-mail.example.com Hello client.example.net ← hyphen: more lines follow
250-SIZE 35882577
250-STARTTLS
250-AUTH LOGIN PLAIN
250 8BITMIME ← space: this is the last line250-STARTTLS and 250 STARTTLS are the same reply code. If a client library appears to hang after EHLO, it is often failing to consume continuation lines until it sees the space-delimited final one.The Five Reply Classes
The first digit tells you the outcome, and it is the only digit you need for the decision that matters most — retry or give up:
| Class | Meaning | What the sender does |
|---|---|---|
2xx | Success — the command was accepted | Continues to the next command |
3xx | Intermediate — more input needed | Sends the data the server is waiting for |
4xx | Transient failure — try again later | Queues the message and retries for hours or days |
5xx | Permanent failure — do not retry | Gives up immediately and bounces to the sender |
A 1xx class exists in the underlying reply-code framework but SMTP does not use it, so in practice you will only ever see the four above.
Why 4xx versus 5xx is the important distinction
A 4xx reply means the server is refusing right now — it is out of disk, rate limiting you, greylisting you, or temporarily unable to verify the recipient. The sending server keeps the message in its queue and retries on a backoff schedule, typically for 24 to 72 hours before giving up. The sender usually receives a "delayed" notice, not a bounce.
A 5xx reply means the server has made a final decision. The message is dead, and the sending server generates a bounce (an NDR) straight away. Retrying an identical message against a 5xx will fail identically every time.
5xx for conditions that are genuinely temporary, and a few return 4xx forever. If a "permanent" failure clears on its own, or a "temporary" one has repeated for a week, trust the observed behaviour over the digit.What the second and third digits add
The second digit categorises the reply: 0 syntax, 1 information, 2 connections, 5 mail system. The third digit is a further refinement with no fixed meaning across servers. In practice nobody parses digits two and three — the enhanced status code below is far more precise.
Enhanced Status Codes (RFC 3463)
The x.y.z code that often follows is a separate, richer scheme. It was added because three digits could not express the range of reasons mail fails:
5.7.1
│ │ │
│ │ └─ Detail — the specific condition within that subject
│ └─── Subject — what part of the system is unhappy
└───── Class — 2 success, 4 persistent transient, 5 permanentThe subject digit is the useful one:
| Subject | Area | Typical example |
|---|---|---|
x.0.z | Other or undefined | 5.0.0 generic permanent failure |
x.1.z | Addressing — sender or recipient | 5.1.1 mailbox does not exist |
x.2.z | Mailbox state | 5.2.2 mailbox full |
x.3.z | Mail system — capacity and resources | 4.3.1 out of storage |
x.4.z | Network and routing | 4.4.1 no answer from host |
x.5.z | Mail delivery protocol | 5.5.1 invalid command |
x.6.z | Message content and media | 5.6.1 media not supported |
x.7.z | Security and policy | 5.7.1 delivery not authorized |
x.7.z security-and-policy subject. If the enhanced code starts with 5.7, the server is not confused — it is refusing on purpose, and the fix is a policy or credential change rather than a retry.Codes You Will Actually See
A working session touches only a handful of codes. These are the ones worth recognising on sight:
| Code | Where it appears | Meaning |
|---|---|---|
220 | On connect, and after STARTTLS | Service ready — the greeting |
221 | After QUIT | Closing the connection |
235 | After AUTH | Authentication succeeded |
250 | EHLO, MAIL FROM, RCPT TO, end of DATA | Command accepted |
334 | During AUTH | Server prompt — send the next base64 token |
354 | After DATA | Start sending the message; end with a lone dot |
421 | Any time | Service unavailable, connection closing — often rate limiting |
450 / 451 | RCPT TO, DATA | Temporarily rejected — greylisting lives here |
452 | RCPT TO, DATA | Insufficient storage, or too many recipients |
454 | STARTTLS, AUTH | TLS not available, or temporary auth failure |
500 / 501 | Any command | Syntax error — usually a broken client |
502 | Any command | Command not implemented |
503 | Any command | Bad sequence — e.g. RCPT TO before MAIL FROM |
530 | MAIL FROM | Authentication required — you skipped AUTH |
535 | AUTH | Credentials rejected |
550 | RCPT TO, DATA | Mailbox unavailable, relaying denied, policy rejection |
552 | DATA | Message too large |
554 | Connect, DATA | Transaction failed — frequently a reputation block |
Greylisting: the 4xx you should not fight
A first-time sender is often met with 451 4.7.1 Greylisted, try again later. The receiver is deliberately deferring an unknown sender on the assumption that spam software will not retry. Legitimate mail servers retry a few minutes later and are then accepted, usually with the sender whitelisted for a period afterwards. Nothing needs fixing — but it does mean a single test send can appear to fail when delivery is working correctly.
Reading a Complete Session
Put together, an entire successful submission reads like this. The codes on the left tell the whole story without any of the text:
220 smtp.example.com ESMTP ready ← server is up
EHLO client.example.net
250-smtp.example.com Hello ← capabilities follow
250-STARTTLS
250 AUTH LOGIN PLAIN
STARTTLS
220 2.0.0 Ready to start TLS ← upgrade accepted
EHLO client.example.net
250-smtp.example.com Hello
250 AUTH LOGIN PLAIN ← re-issued after the upgrade
AUTH LOGIN
334 VXNlcm5hbWU6 ← prompt for username
334 UGFzc3dvcmQ6 ← prompt for password
235 2.7.0 Authentication successful ← credentials accepted
MAIL FROM:<alerts@example.com>
250 2.1.0 Sender OK
RCPT TO:<you@example.org>
250 2.1.5 Recipient OK ← server will take the message
DATA
354 Start mail input; end with <CRLF>.<CRLF>
250 2.0.0 OK: queued as 4F2A81C0B7 ← accepted for delivery
QUIT
221 2.0.0 Bye250 OK: queued means the receiving server has taken responsibility for the message. It can still bounce it afterwards, file it as spam, or discard it on content grounds. Acceptance and delivery are different events.Where You Encounter Reply Codes
In a bounce message
A non-delivery report quotes the receiving server's final reply verbatim. This is the most common place to meet a code, and the quoted line is the authoritative one — the surrounding prose written by your own provider is a paraphrase:
Your message wasn't delivered to you@example.org because the address
couldn't be found.
The response from the remote server was:
550 5.1.1 <you@example.org>: Recipient address rejected: User unknownIn mail server logs
Postfix, Exim and friends log the remote reply against each delivery attempt, which is where you look when mail is queued rather than bounced:
# Postfix — find the reply the remote server gave
grep "status=" /var/log/mail.log | tail -20
# A deferral (4xx) — still queued, will retry
status=deferred (host mx.example.org said: 451 4.7.1 Greylisted)
# A bounce (5xx) — gone
status=bounced (host mx.example.org said: 550 5.7.1 Message rejected)In provider-specific extended codes
Large providers append their own detail digits to the enhanced code. They are not standard, but they are precise and worth searching for verbatim: Google uses 5.7.26 for unauthenticated bulk mail, and Microsoft 365 uses 5.7.139 when SMTP authentication is disabled for the mailbox. When a provider gives you a long code, use it — it usually maps to one specific documented cause.
Turning a Code Into a Fix
Work from the code to the failing step, then to the change that is actually needed:
- 4xx anywhere — usually nothing to fix on your side. Confirm the queue is retrying, and only investigate if it persists past a day.
- 530 5.7.0 — the client sent MAIL FROM without authenticating. Enable SMTP auth in the client, or connect on the submission port.
- 535 5.7.8 — the credential itself is wrong or not the kind the provider expects. See fixing 535 5.7.8.
- 550 5.1.1 — the mailbox does not exist. See user unknown.
- 550 5.7.1 — the server will not carry this message for you. See relaying denied.
- 552 — the message exceeds the server's size limit, which the
SIZEcapability in the EHLO reply tells you in advance. - 554 at connect — your sending IP is blocked outright. Check a blacklist lookup before anything else.
Frequently Asked Questions
Is a 4xx code something I need to fix?
Usually not. The sending server queues the message and retries automatically, and most 4xx conditions — greylisting, rate limiting, a momentarily busy server — clear on their own. It becomes your problem only if the same 4xx repeats for more than about a day, at which point the message will eventually bounce as undeliverable.
What is the difference between the reply code and the enhanced status code?
The three-digit reply code is required by RFC 5321 and tells you the class of outcome. The x.y.z enhanced code from RFC 3463 is optional and tells you the reason with far more precision. When both are present, read the enhanced code — 550 5.1.1 and 550 5.7.1 share a reply code but are completely different problems.
Why did I get a 250 but the email never arrived?
250 at the end of DATA means the receiving server accepted responsibility for the message, not that it reached an inbox. After acceptance it can be filed as spam, quarantined by a content or DMARC policy, or bounced asynchronously to your return path. Check the recipient's spam folder and your bounce address next.
Can I trust the text after the code?
Read it, but never build logic on it. The text is free-form and varies between servers and versions — only the numeric codes are standardised. Log the full line for humans, and branch on the code.
Which codes mean my IP is blocked?
Reputation blocks typically arrive as 554 at connection time or 550 5.7.x at RCPT TO, and the text usually names the blocklist. A 421 can also indicate a rate-based block rather than a listing. Confirm with a blacklist check before requesting delisting.