phacility/phabricator · info · PhabricatorMetaMTAReceivedMailProcessingException
err:duplicate
err:duplicate
Error message
Ignoring email with "Message-ID" hash "%s" that has been seen %d times, including this message.
What it means
dropMailAlreadyReceived() drops inbound mail whose normalized 'Message-ID' hash has already been recorded (STATUS_DUPLICATE): the code counts prior received mails with the same getMessageIDHash() and throws once this copy is not the first. Duplicate suppression is what makes Reply-All and multi-copy delivery safe — the same message hitting several inbound addresses is processed once. Duplicate status is also excluded from bounce emails.
Source
Thrown at src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php:411
return;
}
$first_message = reset($messages);
if ($first_message->getID() == $this->getID()) {
// If this is the first copy of the message, it is okay to process it.
// We may not have been able to to process it immediately when we received
// it, and could may have received several copies without processing any
// yet.
return;
}
$message = pht(
'Ignoring email with "Message-ID" hash "%s" that has been seen %d '.
'times, including this message.',
$message_id_hash,
$messages_count);
throw new PhabricatorMetaMTAReceivedMailProcessingException(
MetaMTAReceivedMailStatus::STATUS_DUPLICATE,
$message);
}
private function dropEmptyMail() {
$body = $this->getCleanTextBody();
$attachments = $this->getAttachments();
if (strlen($body) || $attachments) {
return;
}
// Only send an error email if the user is talking to just Phabricator.
// We can assume if there is only one "To" address it is a Phabricator
// address since this code is running and everything.
$is_direct_mail = (count($this->getToAddresses()) == 1) &&
(count($this->getCCAddresses()) == 0);
View on GitHub (pinned to 5720a38cfe)
Solutions
- Treat STATUS_DUPLICATE as benign in most cases — check MetaMTA > Received Mail and confirm the first copy was processed successfully.
- If you need a genuine second submission, send a fresh message (new Message-ID) rather than resending/forwarding the identical one.
- If a whole queue got marked duplicate after a crash, inspect whether the FIRST copy was processed; if not, requeue that original.
- Admins: verify inbound routing does not fan one message out to multiple Phabricator receive addresses if per-copy processing matters.
Example fix
// before: same Message-ID delivered to two Phabricator addresses // copy 1 -> processed; copy 2 -> err:duplicate, dropped // after (if both actions are truly wanted): send two separate messages // each with its own Message-ID, addressed to the intended object
Defensive patterns
Strategy: try-catch
Validate before calling
// Before submitting, check the Message-ID has not been seen (mirrors dropMailAlreadyReceived):
$hash = PhabricatorMetaMTAReceivedMail::hashMessageID($message_id);
$seen = id(new PhabricatorMetaMTAReceivedMailQuery())
->setViewer($viewer)
->withMessageIDHashes(array($hash))
->execute();
if ($seen) { /* already ingested once: skip */ } Try / catch
try {
$received_mail->processIncomingMail($sender);
} catch (PhabricatorMetaMTAReceivedMailProcessingException $ex) {
if ($ex->getStatusCode() === MetaMTAReceivedMailStatus::STATUS_DUPLICATE) {
// verify the FIRST copy succeeded, then treat as done — never blind-retry
return;
}
throw $ex;
} Prevention
- Submit each new action as a fresh message with its own Message-ID; never resend identical mail.
- Route one inbound address per message; avoid fan-out deliveries of a single Message-ID to several Phabricator handlers.
- Treat duplicate status as idempotency working, and verify the first copy's outcome instead of retrying.
When it happens
Trigger: Processing a received mail whose Message-ID hash already appears in the received-mail table with a processed ancestor. Happens when the MTA delivers one message to multiple Phabricator addresses (To: D123+...@ and CC: bugs@), when a user forwards/re-sends a message keeping the same Message-ID, or when a queue redelivers after a crash.
Common situations: Reply-all to a thread with two Phabricator addresses (expected and harmless — first receiver wins); mail server retry delivering the same message; users dragging the same message into a mail-to-ticket address twice; monitoring that resubmits mail with a stable Message-ID.
Related errors
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/239b7b60c24ef13b.
Report an issue: GitHub.