Unexplained Email::Receiver::InvalidPost errors

Got a few mailing lists mirrored over at https://forum.torproject.org/c/mailing-lists/23

We recently noticed a few messages were not being mirrored from the Mailman3 mailing list to the forum.

The email rejection logs show these emails having encountered a Email::Receiver::InvalidPost error.

The error message logged is either of those two:

We’re sorry, but your email message to [“tor-relays@lists.torproject.org”] (titled [tor-relays] authority bandwidth measurements and latency) didn’t work.

Reason:

Access Denied

If you can correct the problem, please try again.

or:

We’re sorry, but your email message to [“tor-relays@lists.torproject.org”] (titled [tor-relays] Re: webtunnel bridges for the telegram distributor) didn’t work.

Reason:

Something has gone wrong. Perhaps this topic was closed or deleted while you were looking at it?

If you can correct the problem, please try again.

I can’t find anything wrong with these messages from looking at the headers, although in some instances, the extracted body as logged only contains the mailing list footer, or in another instance, it’s a bunch of gibberish characters like there was some decoding glitch.

I’ve tried to reproduce this issue using a test mailing list and test category but was unsuccessful. Any help debugging this would be appreciated.

is “accept email from anonymous accounts” enabled in each category settings, and please could you send Discourse email log (slightly redacted if possible)

1 Like

Yes, I can confirm that this setting is enabled.

and please could you send Discourse email log (slightly redacted if possible)

Is things something I need to extract from the container or the host? We also process mail via the mail-receiver container. Or do you want the logs that are exposed in the Web UI (eg. /admin/email-logs/rejected)?

Did it come from Exchange?

Sometimes Microsoft Exchange will send out garbage if it’s misconfigured to think it’s talking to… I’m not sure - another Exchange server? Something else within its own infrastructure?

You can look at the raw mail from the Discourse console with e.g.:

mid = 'message-id from the log'
puts IncomingEmail.find_by(message_id: mid).raw

This shows the raw email that Discourse received. For example, this message body I just pulled out of our incoming rejection list really is garbage:

This is a multi-part message in MIME format.
--=====003_Dragon855807841081_=====
Content-Type: text/plain;
 charset=utf-8
Content-Transfer-Encoding: base64

7bgir+m+vzzIDCLE0mDmZrfIXvvmXjY=

--=====003_Dragon855807841081_=====
Content-Type: text/html;
 charset=utf-8
Content-Transfer-Encoding: base64

LP/0L4tqmfZizO0DCDDE10uOzMZqzSHDjq04SLPaBjibLVHz+V94m1M45NDN
55aM8SMIf9XY4EFjP9CCFz+ojfmJqmubaz+bjrzmubw+bjWTiGSuLg==

--=====003_Dragon855807841081_=====--

as the parts don’t decode to valid text.

2 Likes

both would be grand. If you use PuTTy SSH you can extract the container logs, and you could snippet the Discourse UI. You can’t search for words in the photo easily though, to redact them😮‍💨

I was able to extract two mails with the full headers. One MUA is Apple Mail and the other is Claws Mail.

I’d be happy to forward those to someone’s private email for debugging, so we avoid pasting it all over the Internet.

In think in both instances it’s likely Discourse that’s not parsing the email content correctly.

For the record, this is still a problem. Discourse regularly drops mailing list messages from various senders with the `Email::Receiver::InvalidPost` error, for reasons I can’t figure out.

If you click on the error in the logs, does it show why in the bounce reason?

e.g.:

If you click on the error in the logs, does it show why in the bounce reason?

Those messages come in two flavors:

We're sorry, but your email message to ["tor-relays@lists.torproject.org"] (titled [tor-relays] Re: abuse report from relays in family 7EAAC49A7840D33B62FA276429F3B03C92AA9327) didn't work.

Reason:

Something has gone wrong. Perhaps this topic was closed or deleted while you were looking at it?

If you can correct the problem, please try again.

I can confirm that no such thing (topic closed or deleted) has happened in these instances.

Some other times, the Reason is simply `Access Denied`.

Edit: the messages are the same as I described in my original post.

Hi lavamind - sorry to bump an old thread, but I wanted to check in first before we go any deeper on debugging.

Are you still seeing the Email::Receiver::InvalidPost rejects on the mailing list mirroring as of now (late 2025 / early 2026)?

If yes, could you share a quick snapshot of:

  • roughly how often it happens (e.g. daily / weekly, % of messages)
  • whether the rejected-email “Reason” is still mainly “Access Denied” vs “topic closed/deleted”
  • whether it’s affecting one list/category or multiple

Once we confirm it’s still occurring, we can move on to collecting the minimum set of diagnostics for a single recent failure (Message-ID + the corresponding stored raw email, etc.).

No need to apologize, I’m very happy you’re looking into this.

Hi lavamind - sorry to bump an old thread, but I wanted to check in first before we go any deeper on debugging.

Are you still seeing the Email::Receiver::InvalidPost rejects on the mailing list mirroring as of now (late 2025 / early 2026)?

Yes, the problem is still occurring.

roughly how often it happens (e.g. daily / weekly, % of messages)

The frequency is difficult to pin down exactly, but the problem affects at least several messages per week, ballpark figure maybe somwehere between 5 to 10% of messages? Some senders seem to be overrepresented in the error logs, so at least it doesn’t look totally random.

whether the rejected-email “Reason” is still mainly “Access Denied” vs “topic closed/deleted”

It’s still a mix of the two.

whether it’s affecting one list/category or multiple

It’s affecting mainly the tor-relays category, but this list receives regular input from multiple senders contrary to the other lists which we mirror, which receive much lower traffic.

Once we confirm it’s still occurring, we can move on to collecting the minimum set of diagnostics for a single recent failure (Message-ID + the corresponding stored raw email, etc.).

We could look into this recent thread. Mailman’s archive indicates it received 5 messages, but the forum mirror topic only has 3 posts in it. The rejection logs contains 3 InvalidPost errors for this topic (2 from the same sender, stangely) and for all of them the rejection reason shown is “Something has gone wrong. Perhaps this topic was closed or deleted while you were looking at it?”

Thanks lavamind - that’s a really helpful data point, especially having a concrete “Mailman archive shows 5, forum topic shows 3” thread to anchor on.

Given you’re seeing this several times per week (ballpark 5-10% of messages) and that, for this incident, the bounce reason is consistently the misleading “topic closed/deleted” variant, the best next step is to capture one failed message end-to-end so we can see what Discourse thought it was doing.


For one of the 3 rejected emails associated with that mirror topic, could you grab the following:

  1. From /admin/email-logs/rejected, open one of the InvalidPost rows for that incident and copy:

    • the Message-ID
    • the date/time
    • the (redacted) To / From / Subject shown in the row
    • the full bounce reason text (as displayed)
  2. From the Rails console (in the Discourse app container), extract the raw email Discourse stored:

@supermathie is IncomingEmail still the canonical place to fetch the raw, and is there anything else you’d want alongside it for InvalidPost debugging?

mid = "<paste the Message-ID from the rejected email log>"
ie  = IncomingEmail.find_by(message_id: mid)

puts ie&.raw
  1. Also pull the paired EmailLog attributes for the same Message-ID (this often reveals whether Discourse treated it as a reply vs a new topic, and what topic/category IDs it resolved):
mid = "<same Message-ID as above>"

log = EmailLog.where(message_id: mid).order(created_at: :desc).first
pp log&.attributes&.slice(
  "id",
  "email_type",
  "to_address",
  "from_address",
  "subject",
  "post_id",
  "topic_id",
  "user_id",
  "status",
  "bounce_error_code",
  "bounce_key",
  "created_at"
)

Why this trio?

  • IncomingEmail.raw tells us if the mail arrived malformed / oddly encoded / footer-only, vs Discourse choosing the “wrong” MIME part.
  • EmailLog tells us what Discourse attempted (new topic vs reply, resolved topic/category ids, etc).
  • the rejected-email UI row confirms we’re looking at the same incident and preserves what operators see.

If privacy is a concern, feel free to redact addresses and any message body text inside raw, but please keep:

  • MIME headers (Content-Type, boundaries, charset, Content-Transfer-Encoding)
  • the multipart structure (so we can see which parts exist)
  • the Message-ID and basic routing headers (you can redact domains if needed)

Once we have one failing Message-ID + IncomingEmail.raw + the EmailLog slice, we should be able to tell quickly whether this is:

  • a reply-key / topic-routing mismatch,
  • a permissions edge case,
  • or an email parsing / MIME-part selection / decoding failure.

Thanks for the very detailed walkthough!

Let’s focus on this message sent to the mailing list topic.

The rejected post log incident headers show this:

Message-ID: <20260103080033.5f6d8f90@dorfdsl.de>
Date: Sat, 03 Jan 2026 08:00:33 +0100
From: Marco Moock via tor-relays <tor-relays@lists.torproject.org>
To: tor-relays@lists.torproject.org
Subject: [tor-relays] Re: Questions about running an exit relay

The full rejection reason text is this:

We're sorry, but your email message to ["tor-relays@lists.torproject.org"] (titled [tor-relays] Re: Questions about running an exit relay) didn't work.

Reason:

Something has gone wrong. Perhaps this topic was closed or deleted while you were looking at it?

If you can correct the problem, please try again.

The plaintext record of the email sent to Discourse from Mailman (IncomingEmail) is available here (I don’t believe it contains any data that’s not already public, but anyway I set that link to expire eventually).

Unfortunately my attempts to extract the EmailLog could not produce any result: it seems like Discourse doesn’t have such a record for that message.

Thanks @lavamind - this is excellent (Message-ID + rejection text + a copy of what Discourse stored).

Also: my bad on the EmailLog ask - it’s very plausible (and maybe expected) that there’s no EmailLog row for an inbound rejection like this. EmailLog is primarily for outgoing mail, whereas inbound mail lives in IncomingEmail (which you’ve already pulled). @supermathie can you confirm I’m not sending folks down the wrong path here?

Given we do have the IncomingEmail, could you paste the metadata fields from that same record? That should give us the “what did Discourse try to do?” context I was hoping to get from EmailLog.

In Rails console:

# @supermathie sanity-check: for inbound InvalidPost debugging, is IncomingEmail metadata the right next thing to collect?

mid = "<20260103080033.5f6d8f90@dorfdsl.de>"
ie  = IncomingEmail.find_by(message_id: mid)

pp ie&.attributes&.slice(
  "id",
  "message_id",
  "from_address",
  "to_addresses",
  "cc_addresses",
  "subject",
  "created_at",
  "user_id",
  "post_id",
  "topic_id",
  "error"
)

# optional: if you’re comfortable, this is often useful too
# (it shows what Discourse extracted as the body vs what it stored raw)
pp ie&.attributes&.slice("raw", "cooked")

(If raw/cooked are huge, feel free to omit them - you’ve already shared the raw.)

Why these fields matter:

  • topic_id / post_id (if present) tells us whether Discourse resolved this as a reply/new post and what it targeted.
  • user_id tells us which staged/real user Discourse mapped the sender to (permissions / “Access Denied” cases).
  • error is often more specific than the generic “topic closed/deleted” bounce text.

If those attributes show topic_id/post_id resolution pointing at the mirror topic, then the likely next step is to work out why the receiver decided it was invalid (empty extracted body, MIME decoding, permissions mismatch, reply-key mismatch, etc). If it shows no topic/post resolution at all, we focus on routing headers / reply detection.

And again - thanks for the paste link. Having a single, concrete failing Message-ID like this is exactly what we needed to stop guessing.

Yes, this is correct. And I agree - it’ll show what was received, what associations Discourse was able to make, and hopefully indicate the next step.

1 Like

Here are the metadata fields for the IncomingObject

{"id"=>9785,
"message_id"=>"20260103080033.5f6d8f90@dorfdsl.de",
"from_address"=>"mm@dorfdsl.de",
"to_addresses"=>"tor-relays@lists.torproject.org",
"cc_addresses"=>"",
"subject"=>"[tor-relays] Re: Questions about running an exit relay",
"created_at"=>2026-01-03 07:03:04.639775000 UTC +00:00,
"user_id"=>10477,
"post_id"=>nil,
"topic_id"=>nil,
"error"=>"Email::Receiver::InvalidPost"}

As for the raw/cooked contents, only the raw property contains the full email with headers. The cooked property doesn’t exist in the object.

Running that through Email::Receiver.new(rawmessage).select_body returns:

=> ["", "", 2]

so I’m pretty confident what’s happening here is that Discourse is incorrectly selecting an empty plain/text portion as the message body, probably this one:

--Sig_/gizYC_1dGsAzUHvksdaMIe2
Content-Type: text/plain;
 charset=UTF-8
Content-Transfer-Encoding: 7bit


which would be an invalid post.

We’ll need to do a bit of investigation on this and probably use it as a test case.

The mail has the structure:

* #<Mail::Part:39500, Multipart: false, Headers: <MIME-Version: 1.0>, <Content-Type: text/plain; charset=us-ascii>, <Content-Transfer-Encoding: 7bit>, <Content-Disposition: inline>, <Content-ID: <6958bf289b75c_b28a46298091029@forum-01-app.mail>>>
  "___…tor-relays mailing list…"
* #<Mail::Part:39520, Multipart: true, Headers: <Content-Type: multipart/signed; boundary="Sig_/gizYC_1dGsAzUHvksdaMIe2"; micalg=pgp-sha256; protocol="application/pgp-signature">, <Content-Transfer-Encoding: 7bit>>
  * #<Mail::Part:39540, Multipart: false, Headers: <Content-Type: text/plain; charset=US-ASCII>, <Content-Transfer-Encoding: quoted-printable>>,
    "On 02.01.2026…"
  * #<Mail::Part:39560, Multipart: false, Headers: <Content-Type: text/plain; charset=UTF-8>, <Content-Transfer-Encoding: 7bit>>,
    ""
  * #<Mail::Part:39580, Multipart: false, Headers: <Content-Type: text/plain; charset=US-ASCII>, <Content-Transfer-Encoding: quoted-printable>>,
    "On 02.01.2026…"
  * #<Mail::Part:39600, Multipart: false, Headers: <Content-Type: text/plain; charset=UTF-8>, <Content-Transfer-Encoding: 7bit>>,
    ""
  * #<Mail::Part:39620, Multipart: false, Headers: <Content-Type: text/plain; charset=US-ASCII>, <Content-Transfer-Encoding: quoted-printable>>,
    "On 02.01.2026…"
  * #<Mail::Part:39640, Multipart: false, Headers: <Content-Type: text/plain; charset=UTF-8>, <Content-Transfer-Encoding: 7bit>>,
    ""
  * #<Mail::Part:39660, Multipart: false, Headers: <Content-Type: text/plain; charset=US-ASCII>, <Content-Transfer-Encoding: quoted-printable>>,
    "On 02.01.2026…"
  * #<Mail::Part:39680, Multipart: false, Headers: <Content-Type: application/pgp-signature>, <Content-Description: Digitale Signatur von OpenPGP>>,
    PGP signature
  * #<Mail::Part:39700, Multipart: false, Headers: <Content-Type: application/pgp-signature>, <Content-Transfer-Encoding: 7bit>, <Content-Description: Digitale Signatur von OpenPGP>>,
    PGP signature
  * #<Mail::Part:39720, Multipart: false, Headers: <Content-Type: application/pgp-signature>, <Content-Transfer-Encoding: 7bit>, <Content-Description: Digitale Signatur von OpenPGP>>]
    PGP signature

(yes, there’s three copies of the actual content, blank content, and the PGP signature)

That first text/plain part is added by the mailing list software and looks like this:

_______________________________________________
tor-relays mailing list -- tor-relays@lists.torproject.org
To unsubscribe send an email to tor-relays-leave@lists.torproject.org

that’s what Discourse (really, the mail gem via .text_part) is choosing as the content of the message:

> puts mail.text_part.to_s
MIME-Version: 1.0
Content-Type: text/plain;
 charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
Content-ID: <6958bf289b75c_b28a46298091029@forum-01-app.mail>

_______________________________________________
tor-relays mailing list -- tor-relays@lists.torproject.org
To unsubscribe send an email to tor-relays-leave@lists.torproject.org

and treated as a blank body with an elided signature.

Were it not for this mostly-blank part added by the mailing list, Discourse would have pulled out the following content for the post:

> We received a court order to preserve the data on the system and were
> forbidden from informing the system owner, which was awkward since
> they had informed the system owner...

Which data did they request?

> Since then I've always run my exit on a separate system on it's own
> IP so if there were a legal demand to turn over "the system" it would
> really only be that system. I'm not a lawyer but I don't think docker
> provides enough isolation for that.

Can they deny you to turn the relay off?
If so, you could then operate a new "system" on another IP.

(elided part below)

On 02.01.2026 18:46 Jon via tor-relays <tor-relays@lists.torproject.org> wrote:

-- 
kind regards
Marco

Send spam to abfall1767375998@stinkedores.dorfdsl.de

I can’t really find fault with Discourse’s mail handling here - if I were called to apportion blame I’d probably start with the mailing list software for effectively adding blank content at the start. That would be better suited at the end, after the real message.

It would have worked with the Mail gem and would also look better in mail clients - here it is in Thunderbird as originally received:

and my “fixed” version (test-fixed.eml.txt (14.3 KB))
with the mailing list signature at the bottom instead of the top:

If you want to see what would have been picked out for post content for a particular IncomingEmail, you can do the following to check:

Email::Receiver.new(IncomingEmail.find(INCOMING_EMAIL_ID).raw).select_body

I’ve reached out to a (human) subscriber of that list and here’s what the raw message body looks like in their MUA with some headers filtered out:

Subject: [tor-relays] Re: Questions about running an exit relay
List-Id: "support and questions about running Tor relays (exit, non-exit,
 bridge)" <tor-relays.lists.torproject.org>
Archived-At: 
 <https://lists.torproject.org/mailman3/hyperkitty/list/tor-relays@lists.torproject.org/message/OAX7EO72GLXS4KPKUG7QSG7EOAR2WYVA/>
List-Archive: 
 <https://lists.torproject.org/mailman3/hyperkitty/list/tor-relays@lists.torproject.org/>
List-Help: <mailto:tor-relays-request@lists.torproject.org?subject=help>
List-Owner: <mailto:tor-relays-owner@lists.torproject.org>
List-Post: <mailto:tor-relays@lists.torproject.org>
List-Subscribe: <mailto:tor-relays-join@lists.torproject.org>
List-Unsubscribe: <mailto:tor-relays-leave@lists.torproject.org>
From: Marco Moock via tor-relays <tor-relays@lists.torproject.org>
Reply-To: Marco Moock <mm@dorfdsl.de>
Content-Type: multipart/mixed; boundary="===============8958541500975114832=="

--===============8958541500975114832==
Content-Type: multipart/signed; boundary="Sig_/gizYC_1dGsAzUHvksdaMIe2";
 protocol="application/pgp-signature"; micalg=pgp-sha256

--Sig_/gizYC_1dGsAzUHvksdaMIe2
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: quoted-printable

On 02.01.2026 18:46 Jon via tor-relays
<tor-relays@lists.torproject.org> wrote:

> We received a court order to preserve the data on the system and were
> forbidden from informing the system owner, which was awkward since
> they had informed the system owner...

Which data did they request?

> Since then I've always run my exit on a separate system on it's own
> IP so if there were a legal demand to turn over "the system" it would
> really only be that system. I'm not a lawyer but I don't think docker
> provides enough isolation for that.

Can they deny you to turn the relay off?
If so, you could then operate a new "system" on another IP.

--=20
kind regards
Marco

Send spam to abfall1767375998@stinkedores.dorfdsl.de

--Sig_/gizYC_1dGsAzUHvksdaMIe2
Content-Type: application/pgp-signature
Content-Description: Digitale Signatur von OpenPGP

-----BEGIN PGP SIGNATURE-----

iQJPBAEBCAA5FiEEpXefSZn9R6zNZtTQE76RLz2tRfAFAmlYvpEbFIAAAAAABAAO
bWFudTIsMi41KzEuMTEsMiwyAAoJEBO+kS89rUXw8kgP/2jkrwfSWHY6EY4WJjn6
EDEqT00pgpwEn9ZpUqLTreS3/ocfHC4g29HIsxpJcj/bH+hNAx96HEz9YmC4JfEt
LDjYc6D+5NBBFQGy0vaJ/LXLQc63CRE/yySSOYxFBZK+uMytNHoZDTjhfRroICbQ
guoO7A4/VuYrGAzCWQkBUmnBjj2LJhuLDW84ObMXhA/EuNy5FIAqyLZxoGmFEfvu
We5d0Hr3+wihzyrgGiG4u8UGFOyL+/PC11CFQyQ0j03cBzhZ5PVdtkqPNHauAcjQ
Gt/HQmaOSGKq0VODRjiHAe5TuRtV6jOfUNgS1Q2vB4FKYmeDQb82ooNfOiJWy3ey
Jpwgg700ppqgZUclpMPlzxKwi2dT/PSO6yYuy+G5sfa0Hxmn5DsQaiSPMTiEP2WC
NwAENYIuHeQOHWiS8B3oVSRW/naLzkmpfChFnTKGsrhLqKQc/iuvv639aHwg9BP7
YEbWbdpFpIU36czfxoTcDYDR1e4JLWryEFKIgo4TIaz4t17NmkxjXB6dHZKLLAdU
AT6LmL6mOTaXe9ewD9pf9Vf2nG0RGVJyZRUDmFzfU0Rx2qi7KdcmmRpZg/2QtJeA
Pmrv8NFuFEL0BrhTvo7C60m+gjLaXPNClgKEN0vkEzjLp/ZKjI9FslP61xUMg8lQ
3xT/HTkNt9uNH2ziBMXLK+5c
=0Euf
-----END PGP SIGNATURE-----

--Sig_/gizYC_1dGsAzUHvksdaMIe2--

--===============8958541500975114832==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
tor-relays mailing list -- tor-relays@lists.torproject.org
To unsubscribe send an email to tor-relays-leave@lists.torproject.org

--===============8958541500975114832==--

There’s no duplication, and the parts are ordered correctly. I’d expect this is closer to what Mailman actually sends out to list subscribers, otherwise if most people would be receiving messages with the footer on top, we’d maybe hear about it?

I’m wondering then if something else in the processing pipeline might be messging with the message body. Our Discourse server first processes incoming email using Postfix which is configured to hand it over to a mail-receiver container, which then hands it over to the Discourse container.

Oh! Given this I’ve gone back and re-investigated… and turns out it’s our problem after all.

From the raw email you posted above, I’ve just discovered that IncomingEmail.raw does not actually store the raw email… we munge it first with Email::Cleaner.

(sorry, this whole time I thought we could trust it, we used to be able to)

This seems to be the Ruby mail gem’s fault… it reorders parts of a mail message when writing it back out:

[1] pry(main)> m = File.open('test.eml').read;
[2] pry(main)> Mail.new(m).parts
=> [
  #<Mail::Part:39680, Multipart: true, Headers: <Content-Type: multipart/signed; boundary="Sig_/gizYC_1dGsAzUHvksdaMIe2"; protocol="application/pgp-signature"; micalg=pgp-sha256>>,
  #<Mail::Part:39700, Multipart: false, Headers: <MIME-Version: 1.0>, <Content-Type: text/plain; charset="us-ascii">, <Content-Transfer-Encoding: 7bit>, <Content-Disposition: inline>>
 ]
[3] pry(main)> Mail.new(Mail.new(m).to_s).parts
=> [
  #<Mail::Part:39720, Multipart: false, Headers: <MIME-Version: 1.0>, <Content-Type: text/plain; charset=us-ascii>, <Content-Transfer-Encoding: 7bit>, <Content-Disposition: inline>, <Content-ID: <6966b4914df79_31d5b1d38126@mars.mail>>>,
  #<Mail::Part:39740, Multipart: true, Headers: <Content-Type: multipart/signed; boundary="Sig_/gizYC_1dGsAzUHvksdaMIe2"; micalg=pgp-sha256; protocol="application/pgp-signature">, <Content-Transfer-Encoding: 7bit>>
]

which causes the problem:

[38] pry(main)> puts Email::Receiver.new(m).select_body[0];
> We received a court order to preserve the data on the system and were
> forbidden from informing the system owner, which was awkward since
> they had informed the system owner...

Which data did they request?

> Since then I've always run my exit on a separate system on it's own
> IP so if there were a legal demand to turn over "the system" it would
> really only be that system. I'm not a lawyer but I don't think docker
> provides enough isolation for that.

Can they deny you to turn the relay off?
If so, you could then operate a new "system" on another IP.

[39] pry(main)> puts Email::Receiver.new(Mail.new(m).to_s).select_body[0];
«no output»
Difference details

test.eml: raw message as provided
test-rubyparsed.eml: message parsed by ruby then turned back into a string
test-pythonparsed.eml: message parsed by python then turned back into a string

--- test.eml	2026-01-13 15:58:18.769489410 -0500
+++ test-rubyparsed.eml	2026-01-13 16:11:17.767312268 -0500
@@ -1,25 +1,46 @@
+Date: Tue, 13 Jan 2026 16:07:21 -0500
+From: Marco Moock via tor-relays <tor-relays@lists.torproject.org>
+Reply-To: Marco Moock <mm@dorfdsl.de>
+Message-ID: <6966b40914be8_31d5b1d38719@mars.mail>
 Subject: [tor-relays] Re: Questions about running an exit relay
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="===============8958541500975114832=="
+Content-Transfer-Encoding: 7bit
 List-Id: "support and questions about running Tor relays (exit, non-exit,
  bridge)" <tor-relays.lists.torproject.org>
-Archived-At: 
- <https://lists.torproject.org/mailman3/hyperkitty/list/tor-relays@lists.torproject.org/message/OAX7EO72GLXS4KPKUG7QSG7EOAR2WYVA/>
-List-Archive: 
- <https://lists.torproject.org/mailman3/hyperkitty/list/tor-relays@lists.torproject.org/>
+Archived-At: <https://lists.torproject.org/mailman3/hyperkitty/list/tor-relays@lists.torproject.org/message/OAX7EO72GLXS4KPKUG7QSG7EOAR2WYVA/>
+List-Archive: <https://lists.torproject.org/mailman3/hyperkitty/list/tor-relays@lists.torproject.org/>
 List-Help: <mailto:tor-relays-request@lists.torproject.org?subject=help>
 List-Owner: <mailto:tor-relays-owner@lists.torproject.org>
 List-Post: <mailto:tor-relays@lists.torproject.org>
 List-Subscribe: <mailto:tor-relays-join@lists.torproject.org>
 List-Unsubscribe: <mailto:tor-relays-leave@lists.torproject.org>
-From: Marco Moock via tor-relays <tor-relays@lists.torproject.org>
-Reply-To: Marco Moock <mm@dorfdsl.de>
-Content-Type: multipart/mixed; boundary="===============8958541500975114832=="
+
+
+--===============8958541500975114832==
+MIME-Version: 1.0
+Content-Type: text/plain;
+ charset=us-ascii
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+Content-ID: <6966b40914ae9_31d5b1d38641@mars.mail>
+
+_______________________________________________
+tor-relays mailing list -- tor-relays@lists.torproject.org
+To unsubscribe send an email to tor-relays-leave@lists.torproject.org
 
 --===============8958541500975114832==
-Content-Type: multipart/signed; boundary="Sig_/gizYC_1dGsAzUHvksdaMIe2";
- protocol="application/pgp-signature"; micalg=pgp-sha256
+Content-Type: multipart/signed;
+ boundary="Sig_/gizYC_1dGsAzUHvksdaMIe2";
+ micalg=pgp-sha256;
+ protocol="application/pgp-signature"
+Content-Transfer-Encoding: 7bit
+
 
 --Sig_/gizYC_1dGsAzUHvksdaMIe2
-Content-Type: text/plain; charset=US-ASCII
+Content-Type: text/plain;
+ charset=US-ASCII
 Content-Transfer-Encoding: quoted-printable
 
 On 02.01.2026 18:46 Jon via tor-relays
@@ -39,7 +60,8 @@
 Can they deny you to turn the relay off?
 If so, you could then operate a new "system" on another IP.
 
---=20
+-- =
+
 kind regards
 Marco
 
@@ -47,6 +69,7 @@
 
 --Sig_/gizYC_1dGsAzUHvksdaMIe2
 Content-Type: application/pgp-signature
+Content-Transfer-Encoding: 7bit
 Content-Description: Digitale Signatur von OpenPGP
 
 -----BEGIN PGP SIGNATURE-----
@@ -69,14 +92,5 @@
 
 --Sig_/gizYC_1dGsAzUHvksdaMIe2--
 
---===============8958541500975114832==
-Content-Type: text/plain; charset="us-ascii"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-Content-Disposition: inline
-
-_______________________________________________
-tor-relays mailing list -- tor-relays@lists.torproject.org
-To unsubscribe send an email to tor-relays-leave@lists.torproject.org
-
 --===============8958541500975114832==--
+
--- test.eml	2026-01-13 15:58:18.769489410 -0500
+++ test-pythonparsed.eml	2026-01-13 16:19:30.385608544 -0500
@@ -1,10 +1,8 @@
 Subject: [tor-relays] Re: Questions about running an exit relay
 List-Id: "support and questions about running Tor relays (exit, non-exit,
  bridge)" <tor-relays.lists.torproject.org>
-Archived-At: 
- <https://lists.torproject.org/mailman3/hyperkitty/list/tor-relays@lists.torproject.org/message/OAX7EO72GLXS4KPKUG7QSG7EOAR2WYVA/>
-List-Archive: 
- <https://lists.torproject.org/mailman3/hyperkitty/list/tor-relays@lists.torproject.org/>
+Archived-At: <https://lists.torproject.org/mailman3/hyperkitty/list/tor-relays@lists.torproject.org/message/OAX7EO72GLXS4KPKUG7QSG7EOAR2WYVA/>
+List-Archive: <https://lists.torproject.org/mailman3/hyperkitty/list/tor-relays@lists.torproject.org/>
 List-Help: <mailto:tor-relays-request@lists.torproject.org?subject=help>
 List-Owner: <mailto:tor-relays-owner@lists.torproject.org>
 List-Post: <mailto:tor-relays@lists.torproject.org>

I opened:

3 Likes