{"record":{"id":"b603d772b19d460f","repo":"SonarSource/sonarqube","slug":"unable-to-contact-bitbucket-cloud-servers-check-y","errorCode":null,"errorMessage":"Unable to contact Bitbucket Cloud servers: Check your credentials","messagePattern":"Unable to contact Bitbucket Cloud servers: Check your credentials","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucket/bitbucketcloud/BitbucketCloudRestClient.java","lineNumber":140,"sourceCode":"      throw new IllegalArgumentException(e.getMessage());\n    }\n  }\n\n  private Token validateAccessToken(String clientId, String clientSecret) {\n    Request request = createAccessTokenRequest(clientId, clientSecret);\n    try (Response response = client.newCall(request).execute()) {\n      if (response.isSuccessful()) {\n        return buildGson().fromJson(response.body().charStream(), Token.class);\n      }\n\n      ErrorDetails errorMsg = getTokenError(response.body(), response.message());\n      if (errorMsg.body != null) {\n        LOG.atInfo().log(() -> String.format(BBC_FAIL_WITH_RESPONSE, response.request().url(), response.code(), errorMsg.body));\n        switch (errorMsg.body) {\n          case \"invalid_grant\":\n            throw new IllegalArgumentException(UNABLE_TO_CONTACT_BBC_SERVERS + \": \" + OAUTH_CONSUMER_NOT_PRIVATE);\n          case \"unauthorized_client\":\n            throw new IllegalArgumentException(UNABLE_TO_CONTACT_BBC_SERVERS + \": \" + UNAUTHORIZED_CLIENT);\n          default:\n            if (errorMsg.parsedErrorMsg != null) {\n              throw new IllegalArgumentException(ERROR_BBC_SERVERS + \": \" + errorMsg.parsedErrorMsg);\n            } else {\n              throw new IllegalArgumentException(UNABLE_TO_CONTACT_BBC_SERVERS);\n            }\n        }\n      } else {\n        LOG.atInfo().log(() -> String.format(BBC_FAIL_WITH_RESPONSE, response.request().url(), response.code(), response.message()));\n      }\n      throw new IllegalArgumentException(UNABLE_TO_CONTACT_BBC_SERVERS);\n\n    } catch (IOException e) {\n      LOG.info(String.format(BBC_FAIL_WITH_ERROR, request.url(), e.getMessage()));\n      throw new IllegalArgumentException(UNABLE_TO_CONTACT_BBC_SERVERS, e);\n    }\n  }\n","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucket/bitbucketcloud/BitbucketCloudRestClient.java#L122-L158","documentation":"During validateAccessToken() the OAuth token exchange at bitbucket.org/site/oauth2/access_token failed with error body field 'error' equal to 'unauthorized_client'; the client translates that into IllegalArgumentException('Unable to contact Bitbucket Cloud servers: Check your credentials'). Bitbucket emits unauthorized_client when the client credentials presented in the Basic Authorization header are rejected, i.e. the Key/Secret pair is wrong, revoked, or the consumer cannot use this grant.","triggerScenarios":"Calling BitbucketCloudRestClient.validate(clientId, clientSecret, workspace): the POST with grant_type=client_credentials and Basic base64(clientId:clientSecret) gets a 40x response whose JSON body has \"error\":\"unauthorized_client\" — hit when the OAuth consumer's Key or Secret is mistyped/stale/rotated, or the consumer does not permit the client_credentials grant.","commonSituations":"Client secret rotated in Bitbucket but the old secret still stored in SonarQube; Key/Secret swapped or pasted with surrounding whitespace/newline; using the consumer Key of a different workspace; SonarQube settings updated to a consumer that was deleted.","solutions":["Re-copy the OAuth consumer's Key and Secret from Bitbucket Workspace Settings > OAuth consumers into SonarQube's Bitbucket Cloud configuration, trimming whitespace/newlines","If the secret was rotated, generate/use the current secret (or reset it in Bitbucket) and save the configuration again","Verify the consumer still exists and is enabled in the workspace whose slug you configured","Retry 'Check configuration' in SonarQube after fixing the credentials"],"exampleFix":"// before\nclientId = \"ASDFGHJK\";            // wrong consumer key\nclientSecret = \"oldRevokedSecret\"; // rotated in Bitbucket\nclient.validate(clientId, clientSecret, workspace);\n// after\nclientId = \"currentConsumerKey\".trim();\nclientSecret = \"currentConsumerSecret\".trim();\nclient.validate(clientId, clientSecret, workspace);","handlingStrategy":"validation","validationCode":"// Pre-verify credentials before saving them in configuration\n// curl -s -X POST -u \"$CLIENT_KEY\":\"$CLIENT_SECRET\" -d grant_type=client_credentials \\\n//   https://bitbucket.org/site/oauth2/access_token\n// \"unauthorized_client\" => key/secret rejected: fix before persisting.\nif (clientId == null || clientId.isBlank() || clientSecret == null || clientSecret.isBlank()) {\n  throw new IllegalArgumentException(\"Bitbucket OAuth client id and secret are required\");\n}\nif (clientId.contains(\"\\n\") || clientSecret.contains(\"\\n\")) {\n  throw new IllegalArgumentException(\"OAuth credentials contain stray newlines; re-copy from Bitbucket\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  client.validate(clientId, clientSecret, workspace);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"Check your credentials\")) {\n    log.error(\"Bitbucket OAuth Key/Secret rejected (unauthorized_client); re-copy from workspace OAuth consumers\");\n  }\n  throw e;\n}","preventionTips":["Store Key/Secret in a secret manager and copy programmatically to avoid trailing whitespace/newlines","If the secret was rotated in Bitbucket, update SonarQube immediately — old secrets fail with unauthorized_client","Verify you are using the Key (client id) and Secret (client secret) of the same consumer in the same workspace","Never swap Key and Secret fields when configuring"],"tags":["bitbucket","oauth","credentials","authentication"],"backgroundTag":"oauth-token-exchange-failed","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"}