SonarSource/sonarqube · error · IllegalArgumentException

Could not parse GitLab answer to verify read permission…

Error message

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

What it means

Thrown by GitlabApplicationClient.checkProjectAccess when the GitLab API returns a 2xx response but the body cannot be parsed as the expected JSON project array (JsonSyntaxException). SonarQube uses the parsed projects to verify the token's read permission on a project.

Solutions

  1. Verify the configured GitLab URL points at the actual GitLab instance root (no trailing path or proxy page)
  2. Test the endpoint manually: curl -H 'PRIVATE-TOKEN: <token>' <gitlabUrl>/projects?membership=true
  3. Check for a proxy/load balancer rewriting responses to HTML error or login pages

Example fix

// before: gitlabUrl = "https://example.com/gitlab/" (proxied HTML)
// after: gitlabUrl = "https://gitlab.example.com";
Defensive patterns

Strategy: try-catch

Validate before calling

curl -sS -o /dev/null -w '%{content_type}' -H "PRIVATE-TOKEN: $TOKEN" "$GITLAB_URL/api/v4/projects?membership=true"  # must be application/json

Try / catch

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

Prevention

When it happens

Trigger: checkReadPermission or checkUrl -> checkProjectAccess gets an HTTP 200 whose body is not valid JSON (HTML error page, empty body, proxy interstitial), causing Gson JsonSyntaxException in Project.parseJsonArray.

Common situations: gitlabUrl points to a non-GitLab server or a reverse proxy returning HTML; wrong path behind a proxy; GitLab returns an unexpected empty 200 body.

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/cae3956664df39a1. Report an issue: GitHub.

Appendix: source

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

  private void checkProjectAccess(@Nullable String gitlabUrl, @Nullable String personalAccessToken, String errorMessage) {
    String url = format("%s/projects", gitlabUrl);

    LOG.debug("get projects : [{}]", url);
    Request.Builder builder = new Request.Builder()
      .url(url)
      .get();

    if (personalAccessToken != null) {
      builder.addHeader(PRIVATE_TOKEN, personalAccessToken);
    }

    Request request = builder.build();

    try (Response response = client.newCall(request).execute()) {
      checkResponseIsSuccessful(response, errorMessage);
      Project.parseJsonArray(response.body().string());
    } catch (JsonSyntaxException e) {
      throw new IllegalArgumentException("Could not parse GitLab answer to verify read permission. Got a non-json payload as result.");
    } catch (IOException e) {
      logException(url, e);
      throw new IllegalArgumentException(errorMessage);
    }
  }

  private static void logException(String url, IOException e) {
    String errorMessage = format("Gitlab API call to [%s] failed with error message : [%s]", url, e.getMessage());
    LOG.info(errorMessage, e);
  }

  public GsonUser checkToken(String gitlabUrl, String personalAccessToken) {
    String url = format("%s/user", gitlabUrl);

    LOG.debug("get current user : [{}]", url);
    Request.Builder builder = new Request.Builder()
      .addHeader(PRIVATE_TOKEN, personalAccessToken)
      .url(url)

View on GitHub (pinned to 184c821202)