JMAP Email/copy retries create duplicate Emails instead of returning alreadyExists

Issue Description

Repeating an identical cross-account Email/copy creates a duplicate Email in Stalwart. This is unsafe when retrying after a lost response: the first copy may have succeeded, but retrying creates another.

Expected Behavior

Return alreadyExists with the first copy’s existingId, as permitted by RFC 8621 §4.7.

Cyrus explicitly implements and tests this behavior:

Actual Behavior

Repeating the same Email/copy creates another destination Email with a new ID.

Reproduction Steps

  1. Create source and destination accounts and a destination mailbox.
  2. Copy one Email between the accounts using Email/copy.
  3. Repeat the same Email/copy in a new request.
  4. Query the destination account and observe two copied Emails.

Relevant Log Output

No server error is emitted. Both requests return a successful created response with different Email IDs.

Stalwart Version

v0.16.x

Installation Method

Docker

Database Backend

PostgreSQL

Blob Storage

S3-compatible

Search Engine

PostgreSQL

Directory Backend

OIDC

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

Confirmed by inspecting the current Email/copy implementation: it does not perform any dedup or idempotency check before copying. The copy_message function in crates/email/src/message/copy.rs only returns CopyMessageError::NotFound or CopyMessageError::OverQuota as failure conditions, there is no lookup for an existing copy with the same source message and no alreadyExists SetError returned anywhere in the copy path (crates/jmap/src/email/copy.rs). Each successful Email/copy call unconditionally ingests a new message and returns a new id, so a retried request will indeed create a duplicate rather than returning the id of the previously created copy.

This matches your reproduction: the behavior you’re describing is accurate for the current codebase, and there’s no existing mechanism implementing the RFC 8621 §4.7 alreadyExists semantics for Email/copy. This is a genuine gap versus the spec’s recommended idempotency handling, not something covered/handled in current docs.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.