phacility/phabricator · error · Exception

This Duo enrollment attempt is invalid or has expired ("%s")

Error message

This Duo enrollment attempt is invalid or has expired ("%s"). Cancel the workflow and try again.

What it means

Thrown inside PhabricatorDuoAuthFactor::processAddFactorForm() while finishing a Duo enrollment. When the user returns to the enrollment screen, Phabricator calls the Duo 'enroll_status' API with the user_id and activation_code stored in the MFA sync token; a 'success' response completes enrollment and 'waiting' re-renders the timer, but 'invalid' (or any unrecognized value) means the pending activation no longer exists on Duo's side, so the workflow aborts.

Source

Thrown at src/applications/auth/factor/PhabricatorDuoAuthFactor.php:252

            return $this->newDuoConfig($user, $duo_user);
          case 'waiting':
            $waiting_icon = id(new PHUIIconView())
              ->setIcon('fa-mobile', 'red');

            $waiting_control = id(new PHUIFormTimerControl())
              ->setIcon($waiting_icon)
              ->setError(pht('Not Complete'))
              ->appendChild(
                pht(
                  'You have not activated this enrollment in the Duo '.
                  'application on your phone yet. Complete activation, then '.
                  'click continue.'));

            $form->appendControl($waiting_control);
            break;
          case 'invalid':
          default:
            throw new Exception(
              pht(
                'This Duo enrollment attempt is invalid or has '.
                'expired ("%s"). Cancel the workflow and try again.',
                $response));
        }
      }
    }

    if ($is_blocked) {
      $blocked_icon = id(new PHUIIconView())
        ->setIcon('fa-times', 'red');

      $blocked_control = id(new PHUIFormTimerControl())
        ->setIcon($blocked_icon)
        ->appendChild(
          pht(
            'Your Duo account ("%s") has not completed Duo enrollment. '.
            'Check your email and complete enrollment to continue.',

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Cancel the enrollment workflow and start over; activate immediately in the Duo phone app after the pairing code appears, since pending Duo activations expire quickly.
  2. Verify the provider's Duo API hostname and credential (Auth > Multi-Factor Auth) so enroll_status reaches the correct Duo integration.
  3. Check network egress to *.duosecurity.com from the web host; MITM proxies can mangle status responses.
  4. If every attempt fails instantly, recreate the Duo integration keys in the Duo admin panel and update the Passphrase credential.
Defensive patterns

Strategy: try-catch

Validate before calling

// Pattern: treat any enroll_status other than 'success'/'waiting' as a
// restart condition before rendering, instead of letting it fatal.
$response = $result['response'];
if (!in_array($response, array('success', 'waiting'), true)) {
  return $this->newRestartEnrollmentControl($provider, $user);
}

Try / catch

try {
  return $factor->processAddFactorForm($provider, $form, $request, $user);
} catch (Exception $ex) {
  if (preg_match('/invalid or has expired/', $ex->getMessage())) {
    // discard the MFA sync token and offer a fresh enrollment
    return $this->newRestartEnrollmentDialog();
  }
  throw $ex;
}

Prevention

When it happens

Trigger: Clicking Continue on the Duo activation-wait screen after the Duo enrollment has expired or been invalidated: the user did not activate in the Duo phone app within Duo's validity window, the activation was consumed or deleted in another session, or the enroll_status call returns an unexpected value because the provider credential/hostname point at the wrong Duo account.

Common situations: User starts enrollment, gets distracted, and clicks Continue minutes later; enrollment tab left open overnight; admin changed the Duo provider credential or hostname between start and finish; Duo returning unexpected payloads through a proxy.

Related errors


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