Anyone managing mail servers regularly encounters outbound messages stuck in the delivery queue without an obvious root cause. Very often, what seems like network flakiness or relay server instability is simply an extra keystroke made by the sender when typing the recipient's address.
However, the way the local Mail Transfer Agent (MTA) and the upstream relay server (smarthost) react to that small mistake exposes an interesting sequence of operations: process forks, C-level DNS lookups, intermediate spam scanners, and local retry databases.
In this guide, we will break down a real log excerpt from Exim to understand the complete lifecycle of a message sent to a mistyped domain, why it does not bounce immediately with a permanent error, and how to clean up that backlog from the terminal.
1. Event log inspection in Exim#
When investigating spooled messages on Linux servers running cPanel, DirectAdmin, or standalone Exim installations, the first place to look is /var/log/exim_mainlog.
The excerpt below shows the trail of a message sent to several valid recipients, plus one mistyped address ([email protected] instead of .com.br):
2026-04-23 10:30:51 1wFu8p-000000062Hd-1Bob <= [email protected] H=(ADMCON037) [186.237.144.42]:58230 P=esmtpsa X=TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256 A=dovecot_login:[email protected] S=49523 [email protected] T="RES: Listagem de cobranca" for [email protected] [email protected] [email protected] [email protected] [email protected] [email protected]
2026-04-23 10:30:51 cwd=/ 13 args: exim -bm -oem -oi -oMr spam-scanner -oMm 1wFu8p-000000062Hd-1Bob -oMai [email protected] -f [email protected] <[email protected]>
2026-04-23 10:30:51 1wFu8p-000000062NQ-2GiD <= [email protected] R=1wFu8p-000000062Hd-1Bob U=mailnull P=spam-scanner S=50322 [email protected] T="RES: Listagem de cobranca" for [email protected]
2026-04-23 10:30:51 1wFu8p-000000062Hd-1Bob <[email protected]>: imunifyemail_spamfilter_transport transport output: action=no action score=-4.359000/6.000000
2026-04-23 10:30:51 1wFu8p-000000062Hd-1Bob => [email protected] R=imunifyemail_spamfilter_router T=imunifyemail_spamfilter_transport
2026-04-23 10:30:52 1wFu8p-000000062NQ-2GiD == [email protected] R=smarthost_auth T=dkim_remote_smtp defer (-44) H=smarthost.domain.com [216.55.99.57]: SMTP error from remote mail server after RCPT TO:<[email protected]>: 450 4.1.2 <[email protected]>: Recipient address rejected: Domain not found
2026-04-23 10:43:06 1wFu8p-000000062NQ-2GiD == [email protected] routing defer (-52): retry time not reached
2026-04-23 10:58:06 1wFu8p-000000062NQ-2GiD == [email protected] R=smarthost_auth T=dkim_remote_smtp defer (-44) H=smarthost.domain.com [216.55.99.57]: SMTP error from remote mail server after RCPT TO:<[email protected]>: 450 4.1.2 <[email protected]>: Recipient address rejected: Domain not found
2. Breaking down the execution flow line by line#
To understand why this message stayed in the spool directory, let us review the four critical stages recorded by the log.
Step 1: Ingress and sender authentication (<=)#
2026-04-23 10:30:51 1wFu8p-000000062Hd-1Bob <= [email protected] H=(ADMCON037) [186.237.144.42]:58230 P=esmtpsa ... A=dovecot_login:[email protected]
In this initial entry, Exim accepts the inbound TCP connection:
- Message identifier: Exim assigns
1wFu8p-000000062Hd-1Bobto track the incoming message in the local spool. - Inbound symbol (
<=): marks that the message was accepted locally. - SASL authentication (
A=dovecot_login): Exim queries Dovecot's authentication socket to verify credentials for[email protected]. Because authentication succeeds, submission proceeds. - Connection security (
P=esmtpsa X=TLS1.2...):esmtpsaindicates an authenticated (a) submission over a TLS-encrypted (s) channel. - Multiple recipients: the sender submitted multiple addresses in the
RCPT TOphase, including valid addresses and the mistyped domain:[email protected].
Step 2: Spam inspection and the child process fork#
2026-04-23 10:30:51 cwd=/ 13 args: exim -bm -oem -oi -oMr spam-scanner ...
2026-04-23 10:30:51 1wFu8p-000000062NQ-2GiD <= [email protected] R=1wFu8p-000000062Hd-1Bob U=mailnull P=spam-scanner ...
Before dispatching the message outward, the mail system passes it through an antispam scanner (such as ImunifyEmail or SpamAssassin):
- Exim launches a child process via
execve()passing-oMr spam-scanner. - The message is inspected and re-injected into the spool under the system user
mailnull. - This new delivery cycle receives its own identifier:
1wFu8p-000000062NQ-2GiD, recording its parent message ID withR=1wFu8p-000000062Hd-1Bob. - The scanner returns a score of
-4.359000(well below the discard threshold of6.0), allowing delivery to proceed.
Step 3: Upstream deferral at the smarthost (defer -44)#
2026-04-23 10:30:52 1wFu8p-000000062NQ-2GiD == [email protected] R=smarthost_auth T=dkim_remote_smtp defer (-44) H=smarthost.domain.com [216.55.99.57]: SMTP error from remote mail server after RCPT TO:<[email protected]>: 450 4.1.2 <[email protected]>: Recipient address rejected: Domain not found
Here is where the delivery stalls:
- Deferral symbol (
==): indicates delivery could not complete in this attempt, but the message remains spooled. - Router and transport (
R=smarthost_auth T=dkim_remote_smtp): rather than querying recipient MX records directly, the local MTA forwards outbound traffic through an authorized smarthost relay (216.55.99.57). - Remote response: during the SMTP handshake, immediately after
RCPT TO:<[email protected]>, the smarthost checks DNS records fordomain.copm.br. Finding no MX or A records, it temporarily rejects the address:
450 4.1.2 Recipient address rejected: Domain not found
Step 4: Exponential backoff and the retry database (defer -52)#
2026-04-23 10:43:06 1wFu8p-000000062NQ-2GiD == [email protected] routing defer (-52): retry time not reached
Roughly 12 minutes later, a scheduled queue runner (exim -q) re-evaluates spooled messages. Exim inspects its local retry database (retry.db) and logs routing defer (-52): retry time not reached.
This confirms that the calculated exponential backoff window has not yet elapsed, preventing useless CPU overhead and redundant network requests to the relay.
3. Why the smarthost replies with 450 instead of 550#
A recurring question in email diagnostics is: if the domain clearly does not exist, why didn't the server reject it outright with a 550 permanent failure?
The explanation lies in RFC 5321 conventions and how relay MTAs implement recipient verification.
Internal DNS check logic in c#
Many smarthosts run Postfix with reject_unknown_recipient_domain enabled inside smtpd_recipient_restrictions.
In Postfix's C source code (smtpd_check.c), domain verification calls standard resolver routines:
/* Simplified DNS lookup routine in smtpd_check.c */
int check_domain_dns(const char *domain) {
unsigned char reply[1024];
int len;
/* 1. Query MX records */
len = res_search(domain, C_IN, T_MX, reply, sizeof(reply));
if (len >= 0) return DNS_FOUND;
/* 2. Fallback to A record (IPv4) */
len = res_search(domain, C_IN, T_A, reply, sizeof(reply));
if (len >= 0) return DNS_FOUND;
/* 3. Evaluate library error codes */
if (h_errno == HOST_NOT_FOUND) {
return DNS_NXDOMAIN;
}
return DNS_RETRY;
}
When this lookup fails, the relay server must choose between:
- Permanent failure (550): the domain definitely does not exist globally.
- Temporary failure (450): the query failed, but it could be caused by temporary packet drops on UDP port 53, resolver timeouts on the relay host, or a newly registered domain still propagating.
Many corporate relays deliberately configure this check to respond with 450 to prevent wrongful permanent bounces during transient DNS hiccups. The side effect is that obvious typos (like .copm.br or .gmial.com) do not bounce back to the sender right away; instead, the email sits in queue retrying for hours or days.
4. Ruling out edge cases before making changes#
Even when a typo appears obvious, it is good practice to rule out two edge cases:
Case 1: Local or upstream DNS resolver failure#
Verify whether the domain is truly non-existent or if your resolvers are failing to reach authoritative nameservers:
# 1. Direct query to global public DNS
dig MX domain.copm.br @8.8.8.8 +trace +nodnssec
# 2. Check for leftover local entries in /etc/hosts
grep -i "domain.copm.br" /etc/hosts
If the response returns status: NXDOMAIN, the domain is definitively unresolvable.
Case 2: Greylisting or rate limiting disguised as a routing error#
Certain enterprise filters return generic recipient rejection strings when a sender exceeds volume thresholds or hits a greylist policy. You can simulate the raw SMTP exchange with netcat:
nc -vv smarthost.domain.com 25 << 'EOF'
EHLO my-server.local
MAIL FROM:<[email protected]>
RCPT TO:<[email protected]>
QUIT
EOF
If the response remains 450 Recipient address rejected, the relay's recipient verification rule is operating as designed.
5. Practical commands to inspect and clear the queue#
Here are the commands to inspect spooled items in Exim and purge them safely.
Inspecting metadata, body, and message logs#
To read the header file in the spool (/var/spool/exim/input/<ID>-H):
exim -Mvh 1wFu8p-000000062NQ-2GiD
To view the raw message body stored on disk (/var/spool/exim/input/<ID>-D):
exim -Mvb 1wFu8p-000000062NQ-2GiD
To list all log entries recorded for this specific message ID:
exim -Mvl 1wFu8p-000000062NQ-2GiD
Checking the Exim retry database#
Exim stores past delivery failures in a binary database at /var/spool/exim/db/retry. To dump its contents:
exim_dumpdb /var/spool/exim retry
To override the backoff timer and force an immediate delivery attempt:
exim -M -v 1wFu8p-000000062NQ-2GiD
The -v flag streams the live SMTP session directly to your terminal.
Purging malformed messages from the queue#
Once you have verified that the address contains an error and other recipients already received their copies, purge the message:
exim -Mrm 1wFu8p-000000062NQ-2GiD
This issues unlink() syscalls on the header (-H) and data (-D) files, freeing disk space and inodes.
If multiple messages are backed up for the same mistyped domain, combine exiqgrep with xargs:
exiqgrep -i -r 'domain.copm.br' | xargs -r exim -Mrm
6. Tracing delivery connections with strace#
If you need to observe the exact network exchange between Exim and the smarthost (including the RCPT TO command and the subsequent 450 reply), attach strace while forcing delivery:
strace -f -s 512 -e trace=network,write,read exim -M 1wFu8p-000000062NQ-2GiD
The flags used here:
-f: tracks child processes created viafork(), necessary because Exim hands off delivery to workers.-s 512: expands string truncation limits so you can view full SMTP commands and responses.-e trace=network,write,read: filters output to network calls (connect(),sendto(),recvfrom()) and read/write operations, removing unrelated library noise.
Practical routine to prevent queue congestion from mistyped domains#
To prevent your mail queue from accumulating dead weight without requiring manual daily cleanup:
- Tune queue expiry parameters: review
ignore_bounce_errors_afterandtimeout_frozen_afterin your Exim configuration so unresolvable messages are automatically discarded after 24 to 48 hours. - Monitor recurring deferred domains: set up a cron check that flags destinations accumulating dozens of deferred entries in
exiqsumm. - Notify senders promptly: when users make common typos (
.copm.br,.con.br,.gmial.com), alert them early so they update their email clients rather than repeatedly resending messages that saturate the retry database.
Was this article helpful?
Leave a quick reaction to help prioritize future technical guides:
This post is licensed under CC BY-NC.



Comments
Join the discussion below.
0 comments