{"record":{"id":"9dabc9ec0d64b652","repo":"phacility/phabricator","slug":"your-account-has-too-many-outstanding-incomplete","errorCode":null,"errorMessage":"Your account has too many outstanding, incomplete MFA synchronization attempts. Wait an hour and try again.","messagePattern":"Your account has too many outstanding, incomplete MFA synchronization attempts\\. Wait an hour and try again\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"warning","filePath":"src/applications/auth/factor/PhabricatorAuthFactor.php","lineNumber":446,"sourceCode":"        ->executeOne();\n    }\n\n    if (!$sync_token) {\n\n      // Don't generate a new sync token if there are too many outstanding\n      // tokens already. This is mostly relevant for push factors like SMS,\n      // where generating a token has the side effect of sending a user a\n      // message.\n\n      $outstanding_limit = 10;\n      $outstanding_tokens = id(new PhabricatorAuthTemporaryTokenQuery())\n        ->setViewer($user)\n        ->withTokenResources(array($user->getPHID()))\n        ->withTokenTypes(array($sync_type))\n        ->withExpired(false)\n        ->execute();\n      if (count($outstanding_tokens) > $outstanding_limit) {\n        throw new Exception(\n          pht(\n            'Your account has too many outstanding, incomplete MFA '.\n            'synchronization attempts. Wait an hour and try again.'));\n      }\n\n      $now = PhabricatorTime::getNow();\n\n      $sync_key = Filesystem::readRandomCharacters(32);\n      $sync_key_digest = PhabricatorHash::digestWithNamedKey(\n        $sync_key,\n        PhabricatorAuthMFASyncTemporaryTokenType::DIGEST_KEY);\n      $sync_ttl = $this->getMFASyncTokenTTL();\n\n      $sync_token = id(new PhabricatorAuthTemporaryToken())\n        ->setIsNewTemporaryToken(true)\n        ->setTokenResource($user->getPHID())\n        ->setTokenType($sync_type)\n        ->setTokenCode($sync_key_digest)","sourceCodeStart":428,"sourceCodeEnd":464,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/auth/factor/PhabricatorAuthFactor.php#L428-L464","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","Complete one of the in-progress synchronizations instead of starting new ones.","As a workaround, an administrator can delete outstanding PhabricatorAuthTemporaryToken rows of the MFA sync type for that user.","For test suites, reset tokens between runs instead of repeatedly starting sync."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Skip generation when too many tokens are already outstanding\n$outstanding = id(new PhabricatorAuthTemporaryTokenQuery())\n  ->setViewer($user)\n  ->withTokenResources(array($user->getPHID()))\n  ->withTokenTypes(array(PhabricatorAuthMFASyncTemporaryTokenType::TOKENTYPE))\n  ->withExpired(false)\n  ->execute();\nif (count($outstanding) > 10) {\n  // show 'wait an hour' message instead of starting a new sync\n}","typeGuard":null,"tryCatchPattern":"try {\n  $result = $factor->processNewEditForm($request, $form);\n} catch (Exception $ex) {\n  if (strpos($ex->getMessage(), 'outstanding') !== false) {\n    // Render a friendly 'wait an hour and try again' dialog; do not retry in a loop.\n    return $this->newDialog()->appendParagraph($ex->getMessage());\n  }\n  throw $ex;\n}","preventionTips":["Complete or abandon one enrollment attempt before starting another; do not reload the sync form repeatedly.","In tests, delete outstanding MFA sync tokens between runs instead of generating new ones.","Treat this as a backoff signal: retry after the token TTL (an hour), never immediately."],"tags":["phabricator","mfa","rate-limit","temporary-token","php"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}