chatmail / chatmail/core

A refused recipient drops the message for the rest of the envelope

Open
#8,640 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Rust
Stars
929
Forks
143
Avg merge
1d 20h
Merged PRs (30d)
53

Description

When chatmail clients are used **with non-relay setups** there is a severe problem: `async-smtp` aborts the transaction at the first refused `RCPT TO`. Core then treats a permanent refusal of a single recipient as a permanent failure for everyone: the chunk loop in `send_msg_to_smtp` stops, the message is marked failed, and [never retried for anyone][deleted]. A transient refusal blocks everyone just the same, but keeps retrying the whole envelope.

Note that chatmail relays accept all recipients, and later asynchronously send a DSN failure message which core processes, except for broadcast channels, where [`handle_ndn` skips them][ndn].

Once [async-smtp reports per-recipient status](https://github.com/chatmail/async-smtp/issues/72), core needs to deal with per- recipient status, dropping permanent failures and accepted recipients from the envelope, and keeping only the transient ones.

Observed 2026-08-29, https://github.com/chatmail/core/pull/8621#pullrequestreview-5057803047 :

```
src/smtp.rs:206: SMTP failed to send: Permanent(Response { ...
message: ["5.1.1 : Recipient address rejected: User unknown in virtual mailbox table"] }).
src/smtp.rs:239: Permanent error, message sending failed.
```

Unrelated but visible in the same log: [on a send failure][markfailed] core loads the message to mark it failed, which for `insert_into_smtp()` tombstones (keyupdates, securejoin) always fails and logs `Failed to load Msg#319115 to mark it as failed: Message Msg#319115 does not exist.` at `error!`.

[pin]: https://github.com/chatmail/core/blob/main/Cargo.toml#L49
[deleted]: https://github.com/chatmail/core/blob/main/src/smtp.rs#L474-L477
[ndn]: https://github.com/chatmail/core/blob/main/src/mimeparser.rs#L2602-L2603
[markfailed]: https://github.com/chatmail/core/blob/main/src/smtp.rs#L308-L326

Contributor guide

Open the contributing guide

Research direction

Start in src/smtp.rs at send_msg_to_smtp and the chunk-loop handling around lines 474-477, then review async-smtp issue 72 for the per-recipient status API. Check the related failure path around lines 308-326 and the broadcast handling in src/mimeparser.rs. Done means permanent refusals and accepted recipients no longer affect the remaining envelope, while transient refusals remain retryable.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend, networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
50/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.