phacility/phabricator · error · PhabricatorApplicationTransactionValidationException

The email address "%s" is already attached to this account.

Error message

The email address "%s" is already attached to this account.

What it means

PhortuneAccountEmailEditor catches the duplicate-key failure from the storage layer when saving a PhortuneAccountEmailAddress whose address already exists on the account, and rethrows it as a PhabricatorApplicationTransactionValidationException with a 'Duplicate' field error naming the offending address. This is a designed validation path: the unique constraint on (account, address) is enforced at the database level and translated into a user-facing transaction error rather than a raw crash.

Source

Thrown at src/applications/phortune/editor/PhortuneAccountEmailEditor.php:33

    return pht('%s created this account email.', $author);
  }

  protected function didCatchDuplicateKeyException(
    PhabricatorLiskDAO $object,
    array $xactions,
    Exception $ex) {

    $errors = array();

    $errors[] = new PhabricatorApplicationTransactionValidationError(
      PhortuneAccountEmailAddressTransaction::TRANSACTIONTYPE,
      pht('Duplicate'),
      pht(
        'The email address "%s" is already attached to this account.',
        $object->getAddress()),
      null);

    throw new PhabricatorApplicationTransactionValidationException($errors);
  }

}

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Before applying, load the account's existing email addresses and skip/flag any address already attached (dedupe on the client of the editor).
  2. Catch PhabricatorApplicationTransactionValidationException in the controller and render getErrors() as form field errors — that is exactly what the exception carries.
  3. Guard the form against double submission (disable the button on submit, or accept a one-time token) so the second insert never happens.
  4. If the address exists but is unverified, resend verification to the existing row instead of creating a second one.

Example fix

// before
id(new PhortuneAccountEmailEditor())
  ->setActor($viewer)
  ->setContentSource($content_source)
  ->applyTransactions($email, $xactions);

// after
$existing = id(new PhortuneAccountEmailQuery())
  ->setViewer($viewer)
  ->withAccountPHIDs(array($account->getPHID()))
  ->withAddresses(array($raw_address))
  ->execute();
if ($existing) {
  $e_address = pht('Duplicate');
  $errors[] = pht(
    'The email address "%s" is already attached to this account.',
    $raw_address);
  return $this->buildEditorResponse($errors);
}
id(new PhortuneAccountEmailEditor())
  ->setActor($viewer)
  ->setContentSource($content_source)
  ->applyTransactions($email, $xactions);
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight: does this address already exist on the account?
$attached = id(new PhortuneAccountEmailQuery())
  ->setViewer($viewer)
  ->withAccountPHIDs(array($account->getPHID()))
  ->withAddresses(array($address))
  ->execute();
if ($attached) {
  // Show the duplicate error in the form; do not apply transactions.
  $e_address = pht('Duplicate');
  $errors[] = pht(
    'The email address "%s" is already attached to this account.',
    $address);
}

Try / catch

try {
  id(new PhortuneAccountEmailEditor())
    ->setActor($viewer)
    ->setContentSource($content_source)
    ->applyTransactions($object, $xactions);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
  // The editor already packaged the duplicate as a field error;
  // re-render the form with $ex->getErrors() instead of crashing.
  return $this->buildFormResponse($object, $ex->getErrors());
}

Prevention

When it happens

Trigger: Applying an edit transaction (TRANSACTIONTYPE on PhortuneAccountEmailAddressTransaction) that adds an address equal to one already attached to the same Phortune account — e.g. re-adding 'billing@example.com' after it was already verified. The catch block fires when insert/update hits the unique index and Lisk raises a duplicate exception.

Common situations: Double form submission or impatient re-click on 'Add Email'; re-inviting an address that is attached but not yet verified; copy-paste retries after a session-expired error page; scripts replaying account-email edits idempotently without deduping.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/30ee440a9d2e6eae. Report an issue: GitHub.