{"record":{"id":"b1268d5ae1a64a7d","repo":"SonarSource/sonarqube","slug":"failed-to-create-github-s-user-access-token","errorCode":null,"errorMessage":"Failed to create GitHub's user access token","messagePattern":"Failed to create GitHub's user access token","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"server/sonar-alm-client/src/main/java/org/sonar/alm/client/github/GithubApplicationClientImpl.java","lineNumber":403,"sourceCode":"        throw new IllegalStateException(\"Failed to create GitHub's user access token. GitHub returned code \" + response.getCode() + \".\");\n      }\n\n      Optional<String> content = response.getContent();\n      Optional<UserAccessToken> accessToken = content.flatMap(c -> Arrays.stream(c.split(\"&\"))\n          .filter(t -> t.startsWith(\"access_token=\"))\n          .map(t -> t.split(\"=\")[1])\n          .findAny())\n        .map(UserAccessToken::new);\n\n      if (accessToken.isPresent()) {\n        return accessToken.get();\n      }\n\n      // If token is not in the 200's body, it's because the client ID or client secret are incorrect\n      LOG.error(\"Failed to create GitHub's user access token. GitHub's response: {}\", content);\n      throw new IllegalArgumentException();\n    } catch (IOException e) {\n      throw new IllegalStateException(\"Failed to create GitHub's user access token\", e);\n    }\n  }\n\n  @Override\n  public GithubAppCredentials convertAppManifest(String apiEndpoint, String code) {\n    String endpoint = \"/app-manifests/\" + URLEncoder.encode(code, StandardCharsets.UTF_8) + \"/conversions\";\n    try {\n      // Unauthenticated call: the GitHub App does not exist yet, so no JWT/app token is available.\n      ApplicationHttpClient.Response response = githubApplicationHttpClient.post(apiEndpoint, null, endpoint);\n\n      if (response.getCode() != HTTP_CREATED && response.getCode() != HTTP_OK) {\n        if (LOG.isDebugEnabled()) {\n          LOG.debug(\"GitHub manifest conversion failed, response body: {}\", response.getContent().orElse(\"\"));\n        }\n        throw new IllegalStateException(\n          \"Failed to create the GitHub App from manifest. GitHub returned code \" + response.getCode());\n      }\n","sourceCodeStart":385,"sourceCodeEnd":421,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-alm-client/src/main/java/org/sonar/alm/client/github/GithubApplicationClientImpl.java#L385-L421","documentation":"In createUserAccessToken, when the HTTP status is 200 but the response body contains no access_token parameter, SonarQube logs the response and throws this IllegalStateException wrapping the IOException path — actually the IllegalArgumentException for a body without a token, and IllegalStateException with this message when an IOException occurs during the exchange. The comment in code notes a missing token in a 2xx body means the Client ID or Client Secret are incorrect.","triggerScenarios":"Calling createUserAccessToken when (a) the body of a successful response lacks 'access_token=' (wrong clientId/clientSecret), producing IllegalArgumentException, or (b) an IOException occurs reading the response, producing this IllegalStateException.","commonSituations":"Client ID / Client Secret copied from the wrong OAuth App, secrets rotated in GitHub but not in SonarQube, whitespace or decryption failure in the stored secret, transient network interruption mid-response.","solutions":["Verify the OAuth App's Client ID and Client Secret in SonarQube match the GitHub OAuth App exactly (regenerate and re-enter the secret).","Check server logs for 'Failed to create GitHub's user access token. GitHub's response:' to see GitHub's actual response body.","Re-run the GitHub authentication flow to get a fresh, unused code (codes are single-use and short-lived).","If caused by IOException, fix connectivity to appUrl and retry."],"exampleFix":"// before (settings)\nclientId=Iv1 WRONGID, clientSecret=<old rotated secret>\n// after\nclientId=Iv1.<correct id from OAuth App>, clientSecret=<current secret regenerated in GitHub>","handlingStrategy":"validation","validationCode":"// verify credentials before the flow\nif (clientId == null || clientId.isBlank()) throw new IllegalArgumentException(\"Client Id required\");\nif (clientSecret == null || clientSecret.isBlank()) throw new IllegalArgumentException(\"Client Secret required\");","typeGuard":null,"tryCatchPattern":"try { client.createUserAccessToken(url, clientId, secret, code); } catch (IllegalArgumentException e) { log.error(\"access_token absent from 2xx body: check Client Id/Secret\"); throw e; } catch (IllegalStateException e) { log.error(\"Token exchange IO failure\", e); throw e; }","preventionTips":["Regenerate the Client Secret after any suspected leak and update SonarQube","Copy Client Id/Secret directly from the GitHub OAuth App page","Rotate codes promptly — they are single-use and expire in ~10 minutes","Log (at DEBUG) the GitHub response body to see the actual rejection reason"],"tags":["github","oauth","credentials","alm-binding"],"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"}