phacility/phabricator · warning · Exception

Your account has too many outstanding, incomplete MFA synchr

Error message

Your account has too many outstanding, incomplete MFA synchronization attempts. Wait an hour and try again.

What it means

When an MFA sync form is rendered without a valid sync key, PhabricatorAuthFactor issues a new PhabricatorAuthTemporaryToken of type PhabricatorAuthMFASyncTemporaryTokenType so the user must synchronize against a server-chosen secret (blocks attacker-chosen TOTP keys). Before minting one it counts unexpired sync tokens for the user; more than 10 outstanding throws this exception. This is chiefly an anti-spam guard for push factors like SMS, where each token generation sends a message.

Source

Thrown at src/applications/auth/factor/PhabricatorAuthFactor.php:446

        ->executeOne();
    }

    if (!$sync_token) {

      // Don't generate a new sync token if there are too many outstanding
      // tokens already. This is mostly relevant for push factors like SMS,
      // where generating a token has the side effect of sending a user a
      // message.

      $outstanding_limit = 10;
      $outstanding_tokens = id(new PhabricatorAuthTemporaryTokenQuery())
        ->setViewer($user)
        ->withTokenResources(array($user->getPHID()))
        ->withTokenTypes(array($sync_type))
        ->withExpired(false)
        ->execute();
      if (count($outstanding_tokens) > $outstanding_limit) {
        throw new Exception(
          pht(
            'Your account has too many outstanding, incomplete MFA '.
            'synchronization attempts. Wait an hour and try again.'));
      }

      $now = PhabricatorTime::getNow();

      $sync_key = Filesystem::readRandomCharacters(32);
      $sync_key_digest = PhabricatorHash::digestWithNamedKey(
        $sync_key,
        PhabricatorAuthMFASyncTemporaryTokenType::DIGEST_KEY);
      $sync_ttl = $this->getMFASyncTokenTTL();

      $sync_token = id(new PhabricatorAuthTemporaryToken())
        ->setIsNewTemporaryToken(true)
        ->setTokenResource($user->getPHID())
        ->setTokenType($sync_type)
        ->setTokenCode($sync_key_digest)

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Wait for the sync token TTL (one hour by default) to expire; expired tokens are ignored by the count and reaped by the garbage collector.
  2. Complete one of the in-progress synchronizations instead of starting new ones.
  3. As a workaround, an administrator can delete outstanding PhabricatorAuthTemporaryToken rows of the MFA sync type for that user.
  4. For test suites, reset tokens between runs instead of repeatedly starting sync.
Defensive patterns

Strategy: retry

Validate before calling

// Skip generation when too many tokens are already outstanding
$outstanding = id(new PhabricatorAuthTemporaryTokenQuery())
  ->setViewer($user)
  ->withTokenResources(array($user->getPHID()))
  ->withTokenTypes(array(PhabricatorAuthMFASyncTemporaryTokenType::TOKENTYPE))
  ->withExpired(false)
  ->execute();
if (count($outstanding) > 10) {
  // show 'wait an hour' message instead of starting a new sync
}

Try / catch

try {
  $result = $factor->processNewEditForm($request, $form);
} catch (Exception $ex) {
  if (strpos($ex->getMessage(), 'outstanding') !== false) {
    // Render a friendly 'wait an hour and try again' dialog; do not retry in a loop.
    return $this->newDialog()->appendParagraph($ex->getMessage());
  }
  throw $ex;
}

Prevention

When it happens

Trigger: The user starts but never completes MFA synchronization more than 10 times within the token TTL (getMFASyncTokenTTL(), typically an hour) - e.g., repeatedly reloading or re-opening the 'add MFA factor' form, each time generating a fresh sync token/SMS that is never confirmed.

Common situations: Users stuck in a loop on the MFA enrollment screen (bad TOTP entry forcing restarts); automated tests or bots hammering the sync endpoint; SMS delivery failures causing retry storms; shared accounts used by several people enrolling simultaneously.

Related errors


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