{"record":{"id":"04ac5e0f8f9ac1d2","repo":"SonarSource/sonarqube","slug":"could-not-parse-gitlab-answer-to-verify-write-perm","errorCode":null,"errorMessage":"Could not parse GitLab answer to verify write permission. Got a non-json payload as result.","messagePattern":"Could not parse GitLab answer to verify write permission\\. Got a non-json payload as result\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabApplicationClient.java","lineNumber":187,"sourceCode":"\n  public void checkWritePermission(String gitlabUrl, String personalAccessToken) {\n    String url = format(\"%s/markdown\", gitlabUrl);\n\n    LOG.debug(\"verify write permission by formating some markdown : [{}]\", url);\n    Request.Builder builder = new Request.Builder()\n      .url(url)\n      .addHeader(PRIVATE_TOKEN, personalAccessToken)\n      .addHeader(\"Content-Type\", MediaTypes.JSON)\n      .post(RequestBody.create(\"{\\\"text\\\":\\\"validating write permission\\\"}\".getBytes(UTF_8)));\n\n    Request request = builder.build();\n\n    String errorMessage = \"Could not validate GitLab write permission. Got an unexpected answer.\";\n    try (Response response = client.newCall(request).execute()) {\n      checkResponseIsSuccessful(response, errorMessage);\n      GsonMarkdown.parseOne(response.body().string());\n    } catch (JsonSyntaxException e) {\n      throw new IllegalArgumentException(\"Could not parse GitLab answer to verify write permission. Got a non-json payload as result.\");\n    } catch (IOException e) {\n      logException(url, e);\n      throw new IllegalArgumentException(errorMessage);\n    }\n\n  }\n\n  private static String urlEncode(String value) {\n    try {\n      return URLEncoder.encode(value, UTF_8.toString());\n    } catch (UnsupportedEncodingException ex) {\n      throw new IllegalStateException(ex.getCause());\n    }\n  }\n\n  protected static void checkResponseIsSuccessful(Response response) throws IOException {\n    checkResponseIsSuccessful(response, \"GitLab Merge Request did not happen, please check your configuration\");\n  }","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabApplicationClient.java#L169-L205","documentation":"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.","triggerScenarios":"checkWritePermission gets an HTTP 200 whose body fails GsonMarkdown.parseOne — HTML page, empty body, or non-standard JSON from a proxy or wrong server.","commonSituations":"gitlabUrl points at a proxy or wrong service returning HTML; GitLab behind a gateway that alters responses; misconfigured URL path.","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"],"exampleFix":"// before: gitlabUrl = \"https://gitlab.example.com/api/v4\" (double path -> wrong response)\n// after: gitlabUrl = \"https://gitlab.example.com\"; // client appends /markdown itself","handlingStrategy":"try-catch","validationCode":"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","typeGuard":null,"tryCatchPattern":"try { gitlabClient.checkWritePermission(url, token); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"non-json\")) { inspectProxyResponseFor(url, \"/markdown\"); } }","preventionTips":["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"],"tags":["gitlab","json","permissions","parsing"],"backgroundTag":"invalid-json-response","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"}