phacility/phabricator · error · Exception

Expected valid JSON response from "user.whoami" request.

Error message

Expected valid JSON response from "user.whoami" request.

What it means

The Phabricator auth adapter lets users log in using another Phabricator install: after OAuth 2 it fetches {base_uri}/api/user.whoami?access_token=... and expects a JSON Conduit response whose 'result' key holds the account data. phutil_json_decode() throwing PhutilJSONParserException means the body was not valid JSON, and it is re-thrown with the original parser exception attached as the cause.

Source

Thrown at src/applications/auth/adapter/PhutilPhabricatorAuthAdapter.php:90

    );
  }

  public function getExtraTokenParameters() {
    return array(
      'grant_type' => 'authorization_code',
    );
  }

  protected function loadOAuthAccountData() {
    $uri = id(new PhutilURI($this->getPhabricatorURI('api/user.whoami')))
      ->replaceQueryParam('access_token', $this->getAccessToken());
    list($body) = id(new HTTPSFuture($uri))->resolvex();

    try {
      $data = phutil_json_decode($body);
      return $data['result'];
    } catch (PhutilJSONParserException $ex) {
      throw new Exception(
        pht(
          'Expected valid JSON response from "user.whoami" request.'),
        $ex);
    }
  }

  private function getPhabricatorURI($path) {
    return rtrim($this->phabricatorBaseURI, '/').'/'.ltrim($path, '/');
  }

}

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Verify the configured Phabricator base URI is the canonical install root (e.g., https://phab.example.com/, not a subpath or alternate vhost).
  2. Manually test: curl 'https://phab.example.com/api/user.whoami?access_token=...' and confirm it returns JSON.
  3. Confirm the access token is a valid Conduit token for the remote install (test it with 'arc' or a Conduit call).
  4. Check for proxies, SSO walls, or SSL interception between the two installs that return HTML before the request reaches Phabricator.

Example fix

// before: base URI includes a path that breaks Conduit routing
$adapter->setPhabricatorURI('https://corp.example.com/phabricator/');

// after: use the canonical install root, then verify externally
$adapter->setPhabricatorURI('https://phab.corp.example.com/');
// curl 'https://phab.corp.example.com/api/user.whoami?access_token=API-...' must return JSON
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the endpoint returns JSON before enabling the provider
$uri = rtrim($base_uri, '/').'/api/user.whoami?access_token='.$token;
list($body) = id(new HTTPSFuture($uri))->resolvex();
if (phutil_json_decode($body) === null) {
  throw new Exception('Base URI does not serve the Conduit API.');
}

Try / catch

try {
  $account_data = $adapter->getAccountData();
} catch (Exception $ex) {
  // The prior PhutilJSONParserException is chained as the cause.
  phlog($ex);
  return $this->newDialog()
    ->setTitle(pht('Invalid Phabricator URI'))
    ->appendParagraph(pht('Expected valid JSON response from "user.whoami".'));
}

Prevention

When it happens

Trigger: setPhabricatorURI() pointing at the wrong vhost, a subpath, or a wiki that returns HTML for /api/user.whoami; the remote install behind a proxy/SSO that answers with an HTML login or error page; a truncated response; the remote Phabricator too old or misconfigured so the endpoint does not exist; a stale cached DNS entry serving a parking page.

Common situations: Base URI configured with a trailing path component that breaks API routing; reverse proxy requiring authentication before the API; remote install upgraded/moved and the old URI now serves HTML; firewall appliances injecting block pages; typo in the domain serving a registrar's parking page.

Related errors


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