SonarSource/sonarqube · error · IllegalStateException

Can not get Bitbucket user profile. HTTP code: %s, response:

Error message

Can not get Bitbucket user profile. HTTP code: %s, response: %s

What it means

Thrown by requestUser when the HTTP request to Bitbucket's `2.0/user` REST endpoint returns a non-success status. It means Bitbucket rejected the authenticated profile lookup, e.g. the OAuth2 access token is invalid, expired, or lacks the account scope. The message embeds the HTTP status code and raw response body for diagnosis.

Source

Thrown at server/sonar-auth-bitbucket/src/main/java/org/sonar/auth/bitbucket/BitbucketIdentityProvider.java:148

    OAuth2AccessToken accessToken = scribe.getAccessToken(code);

    GsonUser gsonUser = requestUser(scribe, accessToken);
    GsonEmails gsonEmails = requestEmails(scribe, accessToken);

    checkTeamRestriction(scribe, accessToken, gsonUser);

    UserIdentity userIdentity = userIdentityFactory.create(gsonUser, gsonEmails);
    context.authenticate(userIdentity);
    context.redirectToRequestedPage();
  }

  private GsonUser requestUser(OAuth20Service service, OAuth2AccessToken accessToken) throws InterruptedException, ExecutionException, IOException {
    OAuthRequest userRequest = new OAuthRequest(Verb.GET, settings.apiURL() + "2.0/user");
    service.signRequest(accessToken, userRequest);
    Response userResponse = service.execute(userRequest);

    if (!userResponse.isSuccessful()) {
      throw new IllegalStateException(format("Can not get Bitbucket user profile. HTTP code: %s, response: %s",
        userResponse.getCode(), userResponse.getBody()));
    }
    String userResponseBody = userResponse.getBody();
    return GsonUser.parse(userResponseBody);
  }

  @CheckForNull
  private GsonEmails requestEmails(OAuth20Service service, OAuth2AccessToken accessToken) throws InterruptedException, ExecutionException, IOException {
    OAuthRequest userRequest = new OAuthRequest(Verb.GET, settings.apiURL() + "2.0/user/emails");
    service.signRequest(accessToken, userRequest);
    Response emailsResponse = service.execute(userRequest);
    if (emailsResponse.isSuccessful()) {
      return GsonEmails.parse(emailsResponse.getBody());
    }
    return null;
  }

  private void checkTeamRestriction(OAuth20Service service, OAuth2AccessToken accessToken, GsonUser user) throws InterruptedException, ExecutionException, IOException {

View on GitHub (pinned to 184c821202)

Solutions

  1. Verify the Bitbucket OAuth consumer has the required scopes (especially account) and that tokens are not expired; re-run the login flow to get a fresh token.
  2. Check sonar.auth.bitbucket.api-url is the correct base ending with a slash (default https://api.bitbucket.org/).
  3. Inspect the HTTP code and response body embedded in the message to identify 4xx (token/scope/config) vs 5xx (Bitbucket-side) causes.
  4. If 5xx, retry after confirming status at status.bitbucket.org.

Example fix

// before
OAuthRequest userRequest = new OAuthRequest(Verb.GET, settings.apiURL() + "2.0/user");
// after (ensure valid base URL with trailing slash)
String base = settings.apiURL().endsWith("/") ? settings.apiURL() : settings.apiURL() + "/";
OAuthRequest userRequest = new OAuthRequest(Verb.GET, base + "2.0/user");
Defensive patterns

Strategy: try-catch

Validate before calling

// Before relying on login, sanity-check the API base URL
if (!settings.apiURL().startsWith("https://api.bitbucket.org")) {
    LOG.warn("Non-default Bitbucket API URL: {}", settings.apiURL());
}

Try / catch

try {
    GsonUser user = requestUser(service, accessToken);
} catch (IllegalStateException e) {
    LOG.error("Bitbucket profile fetch failed: {}", e.getMessage());
    throw new UnauthorizedException("Could not authenticate with Bitbucket");
}

Prevention

When it happens

Trigger: BitbucketIdentityProvider.onCallback flow: after the OAuth2 callback, service.execute(userRequest) on settings.apiURL() + "2.0/user" returns a response with userResponse.isSuccessful() == false (401/403/404/500 etc.), causing the IllegalStateException.

Common situations: Expired or revoked Bitbucket access token; wrong apiURL base (e.g. pointing at Bitbucket Server instead of api.bitbucket.org or missing trailing slash so the path becomes malformed); Bitbucket outage returning 5xx; missing account scope on the OAuth consumer.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/30b153753c69cb03. Report an issue: GitHub.