Issue Description
An IMAP UID MOVE fails with a tagged NO [CONTACTADMIN] internal error if another session changes the same message’s flags at the same moment.
The client is the Nextcloud Mail app, which opens a new IMAP connection for each HTTP request. When you delete a message you have just opened, the app sends two requests together:
- a mark-as-read (
UID STORE +FLAGS (\Seen)) - a delete (
UID MOVEto the trash folder)
Both arrived in the same second on separate connections. The STORE committed first. The MOVE batch still held the message’s older data, so its assertion failed at commit_batch (copy_move.rs:378–380 in v0.16.23). The error was returned as an internal server error.
The STORE path already handles this case in crates/imap/src/op/store.rs (around line 434):
Err(err) if err.is_assertion_failure() => {
items.items.clear();
response.rtype = ResponseType::No;
response.message = "Some messages were modified by another process.".into();
}
The same-account branch of copy_move.rs has no matching handler, so the assertion failure falls through as CONTACTADMIN.
Expected Behavior
A MOVE that loses this race should re-read the message’s current data and retry the commit. Nothing about the flag change makes the move invalid. At minimum, it should return the same clean NO that STORE returns, not an internal error that tells the user to contact the administrator.
Actual Behavior
The MOVE returns NO [CONTACTADMIN]. The message stays in INBOX, and the client shows the user an HTTP 500.
Reproduction Steps
- Open two IMAP sessions for the same account and
SELECT INBOXin both. - In session A, send
UID STORE <uid> +FLAGS (\Seen). In session B, at the same time, sendUID MOVE <uid> Trash. - Repeat until the two commands overlap. Session B then gets
NO [CONTACTADMIN]and the server logsstore.assert-value-failedfromcopy_move.rs.
In real use, this happens when you open a message in Nextcloud Mail and then delete it, which the app turns into a STORE and a MOVE on the same message. I hit it right away while deleting a few emails interactively.
Relevant Log Output
2026-09-26T09:24:23Z ERROR Another process modified the record (store.assert-value-failed) listenerId = "imaps", localPort = 993, remoteIp = 192.168.32.1, remotePort = 44556, causedBy = "crates/store/src/backend/postgres/write.rs:383", id = "5", details = "Internal Server Error", code = "CONTACTADMIN", causedBy = "crates/imap/src/op/copy_move.rs:380"
Stalwart Version
v0.16.x
Installation Method
Docker
Database Backend
PostgreSQL
Blob Storage
S3-compatible
Search Engine
PostgreSQL
Directory Backend
LDAP
Additional Context
- Stalwart v0.16.23 (
stalwartlabs/stalwart:v0.16.23Docker image). Blob storage is RustFS (S3-compatible). The directory is an authentik LDAP outpost. - Client: Nextcloud Mail app on Nextcloud 34.0.3. Its error is
Horde_Imap_Client_Exception_ServerResponsewith response codeCONTACTADMIN, raised duringcopy(..., move: true). - The failing path also exists on
main: the same-account branch ofcopy_move.rshas nois_assertion_failure()handler aroundcommit_batch. - I’m an Open Collective supporter of Stalwart. Thanks for the project.
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