Issue Description
When relaying a message with an empty body, Stalwart serializes it without the terminating blank line after the header block. The receiving MTA can’t separate headers from body, treats everything as the body, and rejects the message — in our case Scaleway TEM replies:
501 5.6.0 "from": missing mandatory header
The From: header is of course present. Messages with a body are fine.
RFC 5322 §2.1 requires the header block to be terminated by an empty line, even when the body is empty.
Captured the raw BDAT payload Stalwart sends, using a local SMTP sink as a relay target. Same client (Apple Mail), same headers, only the body differs:
With body — correct:
...X-Mailer: Apple Mail (2.3864.600.51.1.1)\r\n\r\nI have a body.
hex tail: 29 0d 0a 0d 0a 49 20 68 61 76 65 ...
) \r \n \r \n I h a v e
Empty body — broken:
...X-Mailer: Apple Mail (2.3864.600.51.1.1)\r\n
hex tail: 29 0d 0a <-- message ends here
) \r \n
The final \r\n\r\n is missing. The client submitted the message correctly (with the blank line); it is lost when Stalwart re-serializes the message to add Received: and the DKIM signatures.
Expected Behavior
The email should be delivered
Actual Behavior
The email is rejected with 501 5.6.0 "from": missing mandatory header
Reproduction Steps
- Configure any relay/smarthost route.
- Send a message with a subject but no body text (Apple Mail does this if you leave the text field empty).
- Capture the outgoing stream (e.g. point a relay route at a plaintext SMTP sink) and inspect the bytes.
Expected: message ends with \r\n\r\n. Actual: ends with \r\n.
Stalwart Version
v0.16.x
Installation Method
Docker
Database Backend
RocksDB
Blob Storage
RocksDB
Search Engine
Internal
Directory Backend
Internal
I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.
on
I have searched this support forum (open and closed topics) and confirm this is not a duplicate.
on
I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.
on