Detecting and Processing Bounced Emails in Python
This article describes how to detect, analyze and process bounce messages (delivery failure notifications) with Aspose.Email for Python via .NET.
The aspose.email.bounce module has the classes needed to detect and analyze bounce messages. The main features include:
- Detecting whether an email message is a bounce message
- Extracting the bounce details (recipient, status code, reason, action)
- Accessing the original message from a bounce notification
- Handling different types of Delivery Status Notification (DSN) actions
Core Classes and Methods
| Class/Method | Description |
|---|---|
BounceResult |
Contains the results of the bounce detection |
DSNAction |
Enumeration representing the Delivery Status Notification actions |
MailMessage.check_bounced() |
Method to check if a message is a bounce |
MapiMessage.check_bounced() |
Method to check if an Outlook message is a bounce |
Processing Bounced Emails
It is very common that a message sent to a recipient bounces for some reason, an invalid recipient address, for example. The check_bounced method of the MailMessage class returns a BounceResult object that provides detailed information about the recipient, the action taken and the reason for the notification.
from aspose.email import MailMessage
mail = MailMessage.load(data_dir + "message.eml")
result = mail.check_bounced()
print("IsBounced: " + str(result.is_bounced))
print("Action: " + str(result.action))
print("Recipient: " + str(result.recipient))
print("Reason: " + str(result.reason))
print("Status: " + str(result.status))
Checking if a Message is a Bounce
To determine whether an email message is a bounce message, use the check_bounced() method. It automatically detects bounce messages and extracts the relevant information.
from aspose.email import MailMessage
# Load a message
message = MailMessage.load(data_dir + "message.eml")
# Check if it is a bounce
result = message.check_bounced()
if result.is_bounced:
print("This is a bounce message!")
Extracting Bounce Details
Once a bounce is detected, you can extract the detailed information about the delivery failure:
- recipient: the original recipient address
- status: the DSN status code (
5.1.1,4.2.2, and so on) - reason: a human-readable diagnostic code
- action: the delivery action performed (failed, delayed, and so on)
from aspose.email import MailMessage
message = MailMessage.load(data_dir + "message.eml")
result = message.check_bounced()
if result.is_bounced:
print(f"Failed to: {result.recipient}")
print(f"Status: {result.status}")
print(f"Reason: {result.reason}")
print(f"Action: {result.action}")
Accessing the Original Message
Bounce notifications may include the original message that was returned. It is available through the original_message property of BounceResult:
from aspose.email import MailMessage
message = MailMessage.load(data_dir + "message.eml")
result = message.check_bounced()
if result.original_message is not None:
original = result.original_message
print(f"Original subject: {original.subject}")
print(f"Original from: {original.from_address.address}")
Handling Different DSN Actions
The DSNAction enumeration represents different delivery status notification actions. Each action requires a different response:
| Action | Description | Recommended action |
|---|---|---|
FAILED |
Delivery permanently failed | Remove the recipient from the list |
DELAYED |
Delivery delayed | Retry later |
DELIVERED |
Successfully delivered | No action needed |
EXPANDED |
Expanded to multiple recipients | Monitor for failures |
RELAYED |
Relayed to a non-DSN system | No action needed |
NONE |
No action specified | Investigate further |
from aspose.email import MailMessage
from aspose.email.bounce import DSNAction
message = MailMessage.load(data_dir + "message.eml")
result = message.check_bounced()
if result.action == DSNAction.FAILED:
print("Delivery permanently failed")
# Remove from the mailing list
elif result.action == DSNAction.DELAYED:
print("Delivery delayed")
# Retry later
elif result.action == DSNAction.DELIVERED:
print("Successfully delivered")
Processing Multiple Bounce Messages
To process multiple bounce messages from a directory:
import os
from aspose.email import MailMessage
bounces_dir = data_dir + "bounces"
for file_name in os.listdir(bounces_dir):
if not file_name.endswith(".eml"):
continue
message = MailMessage.load(os.path.join(bounces_dir, file_name))
result = message.check_bounced()
if result.is_bounced:
print(f"Bounce for: {result.recipient}")
DSN Status Codes
Bounce status codes follow the DSN (Delivery Status Notification) format defined in RFC 3463.
Permanent Failures (Start with “5.”)
| Status code | Description |
|---|---|
5.1.1 |
Recipient not found (address not recognized) |
5.1.2 |
Domain not found (DNS lookup failed) |
5.2.1 |
Mailbox disabled (recipient account inactive) |
5.2.2 |
Mailbox full (storage limit exceeded) |
5.3.0 |
Message too large |
5.4.0 |
Routing error |
Temporary Failures (Start with “4.”)
| Status code | Description |
|---|---|
4.2.1 |
Mailbox temporarily disabled |
4.2.2 |
Mailbox full |
4.4.1 |
Connection timeout |
4.4.2 |
Timeout waiting for connection |
4.5.0 |
Temporary server error |
Common Bounce Handling Patterns
Removing Invalid Recipients from a Mailing List
When a bounce has the FAILED action, the recipient address should be removed from your mailing list:
from aspose.email import MailMessage
from aspose.email.bounce import DSNAction
message = MailMessage.load(data_dir + "message.eml")
result = message.check_bounced()
if result.is_bounced and result.action == DSNAction.FAILED:
failed_recipient = result.recipient
# Remove failed_recipient from your mailing list
Retrying Temporary Failures
For temporary failures (the DELAYED action), add the recipient to a retry queue:
from aspose.email import MailMessage
from aspose.email.bounce import DSNAction
message = MailMessage.load(data_dir + "message.eml")
result = message.check_bounced()
if result.action == DSNAction.DELAYED:
# Add result.recipient to the retry queue
pass
Extracting and Saving the Original Message
When the original message is included in the bounce notification, you may want to save it:
import uuid
from aspose.email import MailMessage, SaveOptions
message = MailMessage.load(data_dir + "message.eml")
result = message.check_bounced()
if result.original_message is not None:
original_file_name = f"original_{uuid.uuid4()}.eml"
result.original_message.save(data_dir + original_file_name, SaveOptions.default_eml)
Working with MapiMessage (Outlook MSG Files)
Bounce detection also works with Outlook MSG files through MapiMessage:
from aspose.email.mapi import MapiMessage
msg = MapiMessage.load(data_dir + "bounce.msg")
result = msg.check_bounced()
if result.is_bounced:
print(f"Bounce detected: {result.recipient}")
Troubleshooting
Empty Recipient Field
Some bounce messages do not populate the recipient field. In such cases, check the X-Failed-Recipients header:
from aspose.email import MailMessage
message = MailMessage.load(data_dir + "message.eml")
result = message.check_bounced()
if not result.recipient:
failed_recipients = message.headers.get("X-Failed-Recipients")
if failed_recipients:
# Process the failed recipients taken from the header
print(failed_recipients)
Missing Original Message
Many bounce notifications do not include the original message. Always check whether it is available:
from aspose.email import MailMessage
message = MailMessage.load(data_dir + "message.eml")
result = message.check_bounced()
if result.original_message is None:
print("Original message not included in the bounce notification")
# Extract the recipient from the headers or other sources
BounceResult Properties
| Property | Type | Description |
|---|---|---|
is_bounced |
bool |
True if the message is a bounce message |
action |
DSNAction |
The delivery action performed |
recipient |
str |
The original recipient email address |
status |
str |
The DSN status code (5.1.1, for example) |
reason |
str |
A human-readable diagnostic reason |
original_message |
MailMessage |
The original message, if it is included in the bounce |
Best Practices
- Regular processing: process bounce messages regularly to keep your mailing lists clean.
- Log everything: maintain logs for compliance and troubleshooting.
- Respect temporary failures: implement retry logic for the
DELAYEDbounces. - Immediate removal: remove recipients immediately for the
FAILEDbounces. - Verify headers: check the
X-Failed-Recipientsheader whenrecipientis empty. - Use a dedicated folder: in IMAP, move the bounces to a dedicated folder for easier processing.