Read the raw signal before interpreting it

RFC 4315 adds UID EXPUNGE so a client can permanently remove only messages with specified UIDs that are also marked \Deleted. Plain EXPUNGE operates on the mailbox’s deleted-message state more broadly.

Plain IMAP EXPUNGE operates on messages carrying the `\Deleted` flag in the selected mailbox, which can be hazardous when more than one client or worker is active. Another client may have marked a different message `\Deleted` between your fetch and expunge, so a broad EXPUNGE can remove more than the message your job intended to clean up. The UIDPLUS extension adds `UID EXPUNGE`, allowing a client to restrict expunging to a supplied UID set. This is not merely a performance optimization; it is a concurrency control that lets an automated worker express which deleted messages it owns rather than acting on every deletion visible in shared mailbox state.

A tempting conclusion to avoid

A cleanup worker may believe it “owns” every \Deleted flag because it just set some of them. In a shared mailbox, another client can set deletion state between those commands.

A common batch-cleanup pattern is `STORE +FLAGS \Deleted` followed later by one global EXPUNGE. That assumes no user, phone client, retention rule, or second worker sets the same flag in the meantime. In a one-person sales mailbox it may look safe during testing because only one client is active, then become destructive after the mailbox is added to a mobile device. Do not solve the risk by trusting timing. Check CAPABILITY for UIDPLUS, scope operations to durable UIDs, and if the server lacks selective expunge support, design a safer mailbox policy that avoids automated deletion or isolates the worker’s target messages.

Test the same layer the receiver or server tested

Use two IMAP sessions. Mark different UIDs deleted from each session, then compare the effect of ordinary EXPUNGE with UID EXPUNGE on only one UID set in a disposable test mailbox.

Build a concurrency test with two IMAP sessions. In session A, mark message UID 101 deleted. In session B, mark UID 202 deleted after A has prepared its cleanup. A global EXPUNGE from A can remove both, demonstrating the race. Reset the fixture and use UID EXPUNGE for only 101; UID 202 should remain. Log untagged EXPUNGE responses and verify your local cache updates message state correctly. This scenario makes the difference tangible and also checks that the worker is not confusing sequence numbers with UIDs when constructing the command.

Repair, then repeat the original test

Detect UIDPLUS capability, use UID STORE to mark the intended messages, then UID EXPUNGE exactly those identifiers. If unsupported, serialize destructive cleanup or move messages to a dedicated folder with an explicit operator policy.

For outbound operations, deletion is rarely worth risking reply evidence. Prefer archiving, labeling, or leaving messages intact unless storage or policy requires cleanup. If deletion is required, persist the exact UID set selected by the job, confirm UIDVALIDITY, use UIDPLUS where advertised, and make the action auditable. A worker should never issue a broad expunge simply because it “usually” runs alone. Mailboxes are shared state by design, and a future client can appear without the automation team being told. Conservative cleanup protects the very messages later needed to explain a reply, bounce, or unsubscribe decision.

A realistic low-volume case

Worker A wants to purge UIDs 100:102 while a desktop client has UID 150 marked deleted. A UID EXPUNGE 100:102 should not use 150 merely because the flag already exists.

Before implementing any automated purge, ask whether the job really needs to delete. For deliverability operations, old replies, DSNs, and complaint notices are evidence that can explain future suppression decisions. Moving them to an archive or provider label usually preserves that evidence with less concurrency risk. If retention policy requires deletion, keep a separate queue of intended UIDs and verify the mailbox UIDVALIDITY immediately before applying flags and selective expunge. If the server lacks UIDPLUS, document the reduced safety and consider serializing deletion through one controlled client rather than assuming no other client will mark messages deleted. This makes destructive operations a conscious exception. The safest IMAP automation is often the one that reads narrowly, writes minimally, and leaves cleanup to a clearly owned retention workflow.

Operational telemetry to keep

Record capability response, mailbox UIDVALIDITY, target UIDs, STORE results, expunge responses, and final existence check. Destructive automation needs an audit trail stronger than “command returned OK.”

The safe stopping point

Even UID-targeted deletion is irreversible after expunge. Test against a disposable mailbox and keep bounce/reply retention rules explicit before enabling automatic cleanup.

If a mailbox is also used by a person, document that automation will never permanently delete messages unless a specific retention policy requires it. That convention reduces both technical race risk and support ambiguity. An operator who can still inspect the original reply or DSN has a much better chance of diagnosing why a lead was suppressed than one looking at an audit row after the evidence was expunged.

Field checklist

  • Capture the raw evidence for IMAP UID EXPUNGE concurrent clients safer delete before editing DNS, queue state, or mailbox metadata.
  • Confirm the cited mechanism using uidplus-rfc, imap-rfc rather than a generic deliverability score.
  • Keep a known-good control case so the failing layer can be compared without changing several variables at once.
  • Apply the narrow repair described for UID EXPUNGE vs EXPUNGE: delete only the IMAP messages your cleanup job actually marked and preserve a rollback or retry path.
  • Repeat the original failing test after the change; do not substitute a different checker as proof of recovery.
  • Store timestamp, affected identity, raw result, action taken, and the post-change result in the operator log.

Primary sources

Standards and provider policies can change. These links are the reference points used for this field note.

  1. RFC 4315 — IMAP UIDPLUS ExtensionIETF / RFC EditorUID EXPUNGE and UIDPLUS behavior for deleting only explicitly identified messages in concurrent IMAP workflows.
  2. RFC 9051 — IMAP4rev2IETF / RFC EditorMailbox flags, UIDs, BODY.PEEK and IMAP4rev2 behavior.