phacility/phabricator · error · Exception

Expected '%s' in response!

Error message

Expected '%s' in response!

What it means

readTokenAndTokenSecret() parses every OAuth 1 token response (both the request-token step and the access-token step in finishOAuthHandshake()) and requires an oauth_token entry. If idx($data, 'oauth_token') is empty, the provider returned a 200 body that does not contain a token - typically an error payload, an HTML page, or a differently-formatted response that parseQueryString() could not map.

Source

Thrown at src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php:188

    }

    $validate_uri = $this->getValidateTokenURI();
    $params = array(
      'oauth_verifier' => $this->getVerifier(),
    );

    list($body) = $this->newOAuth1Future($validate_uri, $params)->resolvex();
    $data = id(new PhutilQueryStringParser())->parseQueryString($body);

    $this->readTokenAndTokenSecret($data);

    $this->handshakeData = $data;
  }

  private function readTokenAndTokenSecret(array $data) {
    $token = idx($data, 'oauth_token');
    if (!$token) {
      throw new Exception(pht("Expected '%s' in response!", 'oauth_token'));
    }

    $token_secret = idx($data, 'oauth_token_secret');
    if (!$token_secret) {
      throw new Exception(
        pht("Expected '%s' in response!", 'oauth_token_secret'));
    }

    $this->setToken($token);
    $this->setTokenSecret($token_secret);

    return $this;
  }

  /**
   * Hook that allows subclasses to take actions before the OAuth handshake
   * is completed.
   */

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Log or inspect the raw response body ($body before parseQueryString) to see the provider's actual error message.
  2. Verify the consumer key and consumer secret configured for the adapter.
  3. Check getRequestTokenURI()/getValidateTokenURI() values against the provider's current documentation.
  4. Confirm the adapter's signature method and timestamp handling match what the provider expects.

Example fix

// before: the provider's error body is discarded and the exception is opaque
list($body) = $this->newOAuth1Future($validate_uri, $params)->resolvex();
$data = id(new PhutilQueryStringParser())->parseQueryString($body);

// after: surface the raw body when required parameters are missing
list($body) = $this->newOAuth1Future($validate_uri, $params)->resolvex();
$data = id(new PhutilQueryStringParser())->parseQueryString($body);
if (empty($data['oauth_token'])) {
  throw new Exception(pht('Provider token response was: %s', $body));
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  $adapter->finishOAuthHandshake();
} catch (Exception $ex) {
  phlog(sprintf(
    'OAuth1 token exchange failed for provider "%s": %s',
    $provider_key,
    $ex->getMessage()));
  return $this->newDialog()
    ->setTitle(pht('Authentication Failed'))
    ->appendParagraph($ex->getMessage());
}

Prevention

When it happens

Trigger: The validate-token or request-token endpoint returns 200 with an error body (e.g. 'oauth_problem=...') instead of a token; the body is HTML or JSON, so query-string parsing yields no oauth_token; the consumer key/secret are wrong and the provider signals the error in the body rather than the HTTP status; the response format changed after a provider API upgrade.

Common situations: Bad consumer key or secret on a provider that returns 200 for signature failures; provider endpoint returning JSON instead of a query string; clock skew or signature method mismatch producing provider error pages; the provider renaming endpoints after a version bump.

Related errors


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