phacility/phabricator · error · Exception

Unable to load your OAuth1 token secret from storage. It may

Error message

Unable to load your OAuth1 token secret from storage. It may have expired. Try authenticating again.

What it means

During the OAuth1 callback, Phabricator loads the temporary token row in which it stored the request-token secret at handshake start (PhabricatorAuthTemporaryToken of type PhabricatorOAuth1SecretTemporaryTokenType, looked up with withTokenResources/withTokenTypes/withExpired(false) under the omnipotent user). If executeOne() returns nothing, the secret is gone and the handshake is unrecoverable, so Phabricator throws and tells the user to authenticate again.

Source

Thrown at src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php:257

      ->setTokenExpires(time() + phutil_units('1 hour in seconds'))
      ->setTokenCode($secret)
      ->save();
  }

  private function loadHandshakeTokenSecret($client_code) {
    $secret_type = PhabricatorOAuth1SecretTemporaryTokenType::TOKENTYPE;
    $key = $this->getHandshakeTokenKeyFromClientCode($client_code);
    $type = $this->getTemporaryTokenType($secret_type);

    $token = id(new PhabricatorAuthTemporaryTokenQuery())
      ->setViewer(PhabricatorUser::getOmnipotentUser())
      ->withTokenResources(array($key))
      ->withTokenTypes(array($type))
      ->withExpired(false)
      ->executeOne();

    if (!$token) {
      throw new Exception(
        pht(
          'Unable to load your OAuth1 token secret from storage. It may '.
          'have expired. Try authenticating again.'));
    }

    return $token->getTokenCode();
  }

  private function getTemporaryTokenType($core_type) {
    // Namespace the type so that multiple providers don't step on each
    // others' toes if a user starts Mediawiki and Bitbucket auth at the
    // same time.

    // TODO: This isn't really a proper use of the table and should get
    // cleaned up some day: the type should be constant.

    return $core_type.':'.$this->getProviderConfig()->getID();
  }

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Go back to Phabricator and start authentication again - a fresh handshake stores a new token secret (this is the fix the message itself prescribes)
  2. If it recurs constantly, check the auth temporary-token TTL and garbage collection settings for the install
  3. Ensure only one login handshake per provider is open at a time (close extra tabs on the provider's authorize page)
Defensive patterns

Strategy: retry

Try / catch

try {
  $adapter->setTokenSecret($this->loadHandshakeTokenSecret($client_code));
} catch (Exception $ex) {
  // Token secret expired: only sane recovery is a fresh handshake.
  throw new PhutilAuthUserAbortedException(); // bounces user to login start
}

Prevention

When it happens

Trigger: The temporary token expired between handshake start and provider callback (user sat on the provider's authorize page too long), the token was already consumed by a completed attempt, or the token row was purged/deleted before the callback arrived.

Common situations: A user leaves the authorization screen open for a long time before approving; the user completes login in a different browser or after clearing state; two parallel login attempts for the same provider racing each other.

Understand the failure class

Related errors


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