Issue Description
A cross-account Email/copy with onSuccessDestroyOriginal: true never removes the original. The copy is created, and the follow-up implicit Email/set is correctly issued on the fromAccountId, but its destroy list contains the create-id from the copy request instead of the source Email id. The destroy therefore fails with notFound and the source message is left in place -
a silent duplicate on every move. The create-id is additionally lower-cased before the lookup (ids are treated as base32), which makes the mismatch visible.
Expected Behavior
Per RFC 8621 §5.4, when onSuccessDestroyOriginal is true the server makes a single implicit Email/set on fromAccountId whose destroy argument is the list of source Email ids that were successfully copied. Those originals are destroyed, so a cross-account move leaves no duplicate.
Actual Behavior
The implicit Email/set.destroy receives the create object keys from the Email/copy request (lower-cased), not the source ids. The destroy fails with notFound and the original is never removed.
Reproduction Steps
-
Create a source account (
alice) and a destination account (carol); grantcarolmayRemoveItemson a shared source folder owned byalice. -
Put a message in the source folder; note its id (e.g.
daaaaaay). -
As
carol, copy it across accounts with a distinctive create-key and
onSuccessDestroyOriginal:["Email/copy", { "fromAccountId": "c", // alice "accountId": "e", // carol "create": { "zzTag99": { "id": "daaaaaay", "mailboxIds": { "<carol-inbox>": true }, "keywords": { "$seen": true } } }, "onSuccessDestroyOriginal": true }, "0"] -
Observe the response — the copy succeeds, but the implicit destroy targets the create-key rather than the source id:
["Email/copy", { "created": { "zzTag99": { "id": "iaaaaac", ... } }, ... }, "0"], ["Email/set", { "accountId": "c", "notDestroyed": { "zztag99": { "type": "notFound" } } // <-- lower-cased create-key, not "daaaaaay" }, "0"] -
Email/getonfromAccountIdfordaaaaaayafterwards still returns the message — the original was never destroyed.
Reproduced deterministically (every run) on 0.16.12 and 0.16.14, both directions (shared folder → own account, and between two owners’ shared folders).
Stalwart Version
v0.16.x
Installation Method
Docker
Database Backend
PostgreSQL
Blob Storage
PostgreSQL
Search Engine
PostgreSQL
Directory Backend
SQL
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