SonarSource/sonarqube · error · IllegalArgumentException
Could not parse GitLab answer to verify write permission. Go
Error message
Could not parse GitLab answer to verify write permission. Got a non-json payload as result.
What it means
Thrown by GitlabApplicationClient.checkWritePermission when the GitLab /markdown endpoint returns 2xx but the body is not valid JSON for a GsonMarkdown (JsonSyntaxException). SonarQube uses this endpoint to confirm the token has write-level API access.
Source
Thrown at server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabApplicationClient.java:187
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();
String errorMessage = "Could not validate GitLab write permission. Got an unexpected answer.";
try (Response response = client.newCall(request).execute()) {
checkResponseIsSuccessful(response, errorMessage);
GsonMarkdown.parseOne(response.body().string());
} catch (JsonSyntaxException e) {
throw new IllegalArgumentException("Could not parse GitLab answer to verify write permission. Got a non-json payload as result.");
} catch (IOException e) {
logException(url, e);
throw new IllegalArgumentException(errorMessage);
}
}
private static String urlEncode(String value) {
try {
return URLEncoder.encode(value, UTF_8.toString());
} catch (UnsupportedEncodingException ex) {
throw new IllegalStateException(ex.getCause());
}
}
protected static void checkResponseIsSuccessful(Response response) throws IOException {
checkResponseIsSuccessful(response, "GitLab Merge Request did not happen, please check your configuration");
}View on GitHub (pinned to 184c821202)
Solutions
- Confirm gitlabUrl is the GitLab instance root URL
- Test manually: curl -X POST -H 'PRIVATE-TOKEN: <token>' <gitlabUrl>/api/v4/markdown and inspect the JSON body
- Check proxy/gateway configuration that could replace the JSON body with HTML
Example fix
// before: gitlabUrl = "https://gitlab.example.com/api/v4" (double path -> wrong response) // after: gitlabUrl = "https://gitlab.example.com"; // client appends /markdown itself
Defensive patterns
Strategy: try-catch
Validate before calling
curl -sS -X POST -H "PRIVATE-TOKEN: $TOKEN" -H 'Content-Type: application/json' -d '{"text":"ping","gfm":true}' "$GITLAB_URL/api/v4/markdown" | jq -e '.rendered' # expect JSON Try / catch
try { gitlabClient.checkWritePermission(url, token); } catch (IllegalArgumentException e) { if (e.getMessage().contains("non-json")) { inspectProxyResponseFor(url, "/markdown"); } } Prevention
- Verify gitlabUrl points to the GitLab root; do not pre-append /api/v4
- Ensure proxies/gateways forward GitLab JSON responses unmodified
- Smoke-test the /markdown endpoint with the PAT before saving ALM settings
When it happens
Trigger: checkWritePermission gets an HTTP 200 whose body fails GsonMarkdown.parseOne — HTML page, empty body, or non-standard JSON from a proxy or wrong server.
Common situations: gitlabUrl points at a proxy or wrong service returning HTML; GitLab behind a gateway that alters responses; misconfigured URL path.
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
- Could not parse GitLab answer to verify read permission. Got
- Could not parse GitLab answer to verify token. Got a non-jso
- Could not parse GitLab answer to verify token scopes. Got a
- Response from Azure for request [%s] could not be parsed: [%
- Could not validate GitLab write permission. Got an unexpecte
AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09).
Data as JSON: /api/errors/04ac5e0f8f9ac1d2.
Report an issue: GitHub.