phacility/phabricator · error · Exception

Expected '%s' in request!

Error message

Expected '%s' in request!

What it means

Thrown on the final (callback) leg of an OAuth1 handshake in PhabricatorOAuth1AuthProvider. After the user authorizes at the provider, the redirect back to Phabricator must carry an 'oauth_token' query parameter alongside 'oauth_verifier'; Phabricator reads both with $request->getStr('oauth_token') and throws immediately if the token is absent. Without it the request-token secret saved at handshake start cannot be matched, so the handshake cannot proceed.

Source

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

    }

    $denied = $request->getStr('denied');
    if (strlen($denied)) {
      // Twitter indicates that the user cancelled the login attempt by
      // returning "denied" as a parameter.
      throw new PhutilAuthUserAbortedException();
    }

    // NOTE: You can get here via GET, this should probably be a bit more
    // user friendly.

    $this->verifyAuthCSRFCode($request, $controller->getExtraURIData());

    $token = $request->getStr('oauth_token');
    $verifier = $request->getStr('oauth_verifier');

    if (!$token) {
      throw new Exception(pht("Expected '%s' in request!", 'oauth_token'));
    }

    if (!$verifier) {
      throw new Exception(pht("Expected '%s' in request!", 'oauth_verifier'));
    }

    $adapter->setToken($token);
    $adapter->setVerifier($verifier);

    $client_code = $this->getAuthCSRFCode($request);
    $token_secret = $this->loadHandshakeTokenSecret($client_code);
    $adapter->setTokenSecret($token_secret);

    // NOTE: As a side effect, this will cause the OAuth adapter to request
    // an access token.

    try {
      $identifiers = $adapter->getAccountIdentifiers();

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Restart authentication from the Phabricator login screen (/auth/start/) so a fresh handshake with new request token begins
  2. Verify the callback/consumer URL configured in the provider's application settings exactly matches the callback Phabricator shows in the auth provider config
  3. Check the provider's redirect for a denial indicator (e.g. 'denied=' instead of oauth_token) and handle denial as a user-abort rather than a callback
  4. Confirm nothing between the provider and Phabricator (proxy, rewrite rule) strips query string parameters
Defensive patterns

Strategy: validation

Validate before calling

// Before letting the OAuth1 provider process the callback:
$token = $request->getStr('oauth_token');
$verifier = $request->getStr('oauth_verifier');
if (!strlen($token) || !strlen($verifier)) {
  // Not a valid provider redirect: treat as aborted handshake,
  // e.g. redirect back to a fresh /auth/start/ flow instead of fataling.
  return id(new AphrontRedirectResponse())->setURI('/auth/start/');
}

Try / catch

try {
  $result = $provider->processLoginRequest($request, $controller);
} catch (Exception $ex) {
  // Surface pht("Expected '%s' in request!") as a friendly
  // 'restart authentication' dialog instead of an unhandled fatal.
  return $this->newDialog()->setTitle(pht('Authentication Failed'))
    ->appendChild(pht('Restart the login flow and try again.'))
    ->addSubmitButton(pht('Retry'));
}

Prevention

When it happens

Trigger: The provider redirects to the Phabricator auth callback without ?oauth_token=...: the user denied the authorization prompt and the provider sent a denial parameter instead, the user bookmarked/reloaded/manually edited the callback URL, or the provider application's configured callback URL drops query parameters.

Common situations: Bitbucket/MediaWiki/JIRA-style OAuth1 apps with a misconfigured callback URL; a user refreshing the callback page after already completing login; starting the handshake in one browser tab and pasting the callback into another.

Related errors


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