New articles: - Python smtplib: Missing Date/Message-ID Headers Break Mail Clients - Fantastical MCP: Permission Denied (macOS Quarantine) - Ubuntu dist-upgrade Repo Quarantine Updated: troubleshooting index, SUMMARY.md nav, WOL article edits. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
60 lines
2.3 KiB
Markdown
60 lines
2.3 KiB
Markdown
---
|
|
title: "Python smtplib: Missing Date/Message-ID Headers Break Mail Clients"
|
|
domain: troubleshooting
|
|
category: general
|
|
tags: [email, python, smtplib, spam, rfc, spark]
|
|
status: published
|
|
created: 2026-04-29
|
|
updated: 2026-04-29
|
|
---
|
|
# Python smtplib: Missing Date/Message-ID Headers Break Mail Clients
|
|
|
|
## Problem
|
|
|
|
Emails sent via Python's `smtplib` and `EmailMessage` appear on some mail clients but not others. The emails are delivered to the server and visible in Maildir, but specific clients silently suppress them.
|
|
|
|
## Root Cause
|
|
|
|
Python's `EmailMessage` does **not** automatically add `Date:` or `Message-ID:` headers. These are required by RFC 5322. Without them:
|
|
|
|
- **SpamAssassin** flags `MISSING_DATE` and `MISSING_MID`, and may set `X-Spam-Flag: YES` even if the overall score is below the spam threshold
|
|
- **Mail clients** (e.g., Spark) may filter on the spam flag header and silently hide the message — no Junk folder, just invisible
|
|
- **Other clients** (e.g., iPhone Mail, some Spark builds) may be more lenient and display the message anyway
|
|
|
|
This creates a confusing situation where the same email appears on one device but not another, despite both using the same IMAP account.
|
|
|
|
## Fix
|
|
|
|
Always include `Date` and `Message-ID` headers when constructing emails with `EmailMessage`:
|
|
|
|
```python
|
|
import smtplib
|
|
from email.message import EmailMessage
|
|
from email.utils import formatdate, make_msgid
|
|
|
|
msg = EmailMessage()
|
|
msg['Subject'] = 'Your subject here'
|
|
msg['From'] = 'sender@example.com'
|
|
msg['To'] = 'recipient@example.com'
|
|
msg['Date'] = formatdate(localtime=True)
|
|
msg['Message-ID'] = make_msgid(domain='example.com')
|
|
msg.set_content('Email body here')
|
|
|
|
with smtplib.SMTP('mail.example.com', 25) as s:
|
|
s.send_message(msg)
|
|
```
|
|
|
|
## Verification
|
|
|
|
After applying the fix, check that SpamAssassin no longer flags the headers:
|
|
|
|
```bash
|
|
# Check email headers on the mail server
|
|
grep -E 'MISSING_DATE|MISSING_MID|X-Spam' /var/vmail/domain/user/cur/<message-file>
|
|
```
|
|
|
|
A clean message should show `X-Spam-Status: No` with no `MISSING_DATE` or `MISSING_MID` in the test list.
|
|
|
|
## Key Takeaway
|
|
|
|
Python's `EmailMessage` is a low-level builder — it trusts you to set all required headers. Unlike higher-level mail libraries or webmail interfaces, it will happily send a message with no date or message ID. Always add both explicitly in any script that sends email via `smtplib`.
|