SonarSource/sonarqube · error · IllegalArgumentException

Could not parse GitLab answer to verify token scopes. Got a

Error message

Could not parse GitLab answer to verify token scopes. Got a non-json payload as result.

What it means

Thrown by GitlabApplicationClient.getPersonalAccessTokenInfo when the GitLab /personal_access_tokens/self endpoint returns 2xx but the body is not valid JSON for a GsonPersonalAccessTokenInfo (JsonSyntaxException). Used to validate the token's scopes.

Source

Thrown at server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabApplicationClient.java:163

    }
  }

  public GsonPersonalAccessTokenInfo getPersonalAccessTokenInfo(String gitlabUrl, String personalAccessToken) {
    String url = format("%s/personal_access_tokens/self", gitlabUrl);

    LOG.debug("get personal access token info : [{}]", url);
    Request request = new Request.Builder()
      .addHeader(PRIVATE_TOKEN, personalAccessToken)
      .url(url)
      .get()
      .build();

    String errorMessage = "Could not validate GitLab token scopes. Got an unexpected answer.";
    try (Response response = client.newCall(request).execute()) {
      checkResponseIsSuccessful(response, errorMessage);
      return GsonPersonalAccessTokenInfo.parseOne(response.body().string());
    } catch (JsonSyntaxException e) {
      throw new IllegalArgumentException("Could not parse GitLab answer to verify token scopes. Got a non-json payload as result.");
    } catch (IOException e) {
      logException(url, e);
      throw new IllegalArgumentException(errorMessage);
    }
  }

  public void checkWritePermission(String gitlabUrl, String personalAccessToken) {
    String url = format("%s/markdown", gitlabUrl);

    LOG.debug("verify write permission by formating some markdown : [{}]", url);
    Request.Builder builder = new Request.Builder()
      .url(url)
      .addHeader(PRIVATE_TOKEN, personalAccessToken)
      .addHeader("Content-Type", MediaTypes.JSON)
      .post(RequestBody.create("{\"text\":\"validating write permission\"}".getBytes(UTF_8)));

    Request request = builder.build();

View on GitHub (pinned to 184c821202)

Solutions

  1. Upgrade GitLab to a version that supports GET /personal_access_tokens/self (GitLab 16+)
  2. Verify manually: curl -H 'PRIVATE-TOKEN: <token>' <gitlabUrl>/api/v4/personal_access_tokens/self
  3. Confirm gitlabUrl points to the GitLab instance and not a proxy returning HTML

Example fix

// before: GitLab 15.x (endpoint returns unexpected body) 
// after: upgrade GitLab to >=16.0 or use a SonarQube version matching your GitLab
Defensive patterns

Strategy: try-catch

Validate before calling

curl -sS -H "PRIVATE-TOKEN: $TOKEN" "$GITLAB_URL/api/v4/personal_access_tokens/self" | jq -e '.scopes'  # requires GitLab 16+

Try / catch

try { gitlabClient.getPersonalAccessTokenInfo(url, token); } catch (IllegalArgumentException e) { if (e.getMessage().contains("non-json")) { checkGitlabVersionSupportsSelfEndpoint(); } }

Prevention

When it happens

Trigger: getPersonalAccessTokenInfo receives an HTTP 200 whose body fails GsonPersonalAccessTokenInfo.parseOne — HTML response, empty body, or unexpected JSON shape (e.g. older GitLab lacking this endpoint variant).

Common situations: GitLab version too old to support /personal_access_tokens/self (added in GitLab 16); proxy interstitial; URL pointing at a non-GitLab server.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


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