{"record":{"id":"30b153753c69cb03","repo":"SonarSource/sonarqube","slug":"can-not-get-bitbucket-user-profile-http-code-s","errorCode":null,"errorMessage":"Can not get Bitbucket user profile. HTTP code: %s, response: %s","messagePattern":"Can not get Bitbucket user profile\\. HTTP code: (.+?), response: (.+?)","errorType":"http","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"server/sonar-auth-bitbucket/src/main/java/org/sonar/auth/bitbucket/BitbucketIdentityProvider.java","lineNumber":148,"sourceCode":"    OAuth2AccessToken accessToken = scribe.getAccessToken(code);\n\n    GsonUser gsonUser = requestUser(scribe, accessToken);\n    GsonEmails gsonEmails = requestEmails(scribe, accessToken);\n\n    checkTeamRestriction(scribe, accessToken, gsonUser);\n\n    UserIdentity userIdentity = userIdentityFactory.create(gsonUser, gsonEmails);\n    context.authenticate(userIdentity);\n    context.redirectToRequestedPage();\n  }\n\n  private GsonUser requestUser(OAuth20Service service, OAuth2AccessToken accessToken) throws InterruptedException, ExecutionException, IOException {\n    OAuthRequest userRequest = new OAuthRequest(Verb.GET, settings.apiURL() + \"2.0/user\");\n    service.signRequest(accessToken, userRequest);\n    Response userResponse = service.execute(userRequest);\n\n    if (!userResponse.isSuccessful()) {\n      throw new IllegalStateException(format(\"Can not get Bitbucket user profile. HTTP code: %s, response: %s\",\n        userResponse.getCode(), userResponse.getBody()));\n    }\n    String userResponseBody = userResponse.getBody();\n    return GsonUser.parse(userResponseBody);\n  }\n\n  @CheckForNull\n  private GsonEmails requestEmails(OAuth20Service service, OAuth2AccessToken accessToken) throws InterruptedException, ExecutionException, IOException {\n    OAuthRequest userRequest = new OAuthRequest(Verb.GET, settings.apiURL() + \"2.0/user/emails\");\n    service.signRequest(accessToken, userRequest);\n    Response emailsResponse = service.execute(userRequest);\n    if (emailsResponse.isSuccessful()) {\n      return GsonEmails.parse(emailsResponse.getBody());\n    }\n    return null;\n  }\n\n  private void checkTeamRestriction(OAuth20Service service, OAuth2AccessToken accessToken, GsonUser user) throws InterruptedException, ExecutionException, IOException {","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-auth-bitbucket/src/main/java/org/sonar/auth/bitbucket/BitbucketIdentityProvider.java#L130-L166","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","Check sonar.auth.bitbucket.api-url is the correct base ending with a slash (default https://api.bitbucket.org/).","Inspect the HTTP code and response body embedded in the message to identify 4xx (token/scope/config) vs 5xx (Bitbucket-side) causes.","If 5xx, retry after confirming status at status.bitbucket.org."],"exampleFix":"// before\nOAuthRequest userRequest = new OAuthRequest(Verb.GET, settings.apiURL() + \"2.0/user\");\n// after (ensure valid base URL with trailing slash)\nString base = settings.apiURL().endsWith(\"/\") ? settings.apiURL() : settings.apiURL() + \"/\";\nOAuthRequest userRequest = new OAuthRequest(Verb.GET, base + \"2.0/user\");","handlingStrategy":"try-catch","validationCode":"// Before relying on login, sanity-check the API base URL\nif (!settings.apiURL().startsWith(\"https://api.bitbucket.org\")) {\n    LOG.warn(\"Non-default Bitbucket API URL: {}\", settings.apiURL());\n}","typeGuard":null,"tryCatchPattern":"try {\n    GsonUser user = requestUser(service, accessToken);\n} catch (IllegalStateException e) {\n    LOG.error(\"Bitbucket profile fetch failed: {}\", e.getMessage());\n    throw new UnauthorizedException(\"Could not authenticate with Bitbucket\");\n}","preventionTips":["Ensure the Bitbucket OAuth consumer has the account scope.","Verify api-url ends with a slash and points to the correct Bitbucket host.","Monitor status.bitbucket.org for outages before blaming configuration."],"tags":["oauth","bitbucket","http-error","authentication"],"backgroundTag":"http-error-response","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}