{"record":{"id":"bb92c18662238f86","repo":"SonarSource/sonarqube","slug":"pagination-data-from-gitlab-response-is-missing","errorCode":null,"errorMessage":"Pagination data from GitLab response is missing","messagePattern":"Pagination data from GitLab response is missing","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabApplicationClient.java","lineNumber":415,"sourceCode":"      Headers headers = response.headers();\n      checkResponseIsSuccessful(response, \"Could not get projects from GitLab instance\");\n      List<Project> projectList = Project.parseJsonArray(response.body().string());\n      int returnedPageNumber = parseAndGetIntegerHeader(headers.get(\"X-Page\"));\n      int returnedPageSize = parseAndGetIntegerHeader(headers.get(\"X-Per-Page\"));\n      String xtotal = headers.get(\"X-Total\");\n      Integer totalProjects = Strings.isEmpty(xtotal) ? null : parseAndGetIntegerHeader(xtotal);\n      return new ProjectList(projectList, returnedPageNumber, returnedPageSize, totalProjects);\n    } catch (JsonSyntaxException e) {\n      throw new IllegalArgumentException(\"Could not parse GitLab answer to search projects. Got a non-json payload as result.\");\n    } catch (IOException e) {\n      logException(url, e);\n      throw new IllegalStateException(e.getMessage(), e);\n    }\n  }\n\n  private static int parseAndGetIntegerHeader(@Nullable String header) {\n    if (header == null) {\n      throw new IllegalArgumentException(\"Pagination data from GitLab response is missing\");\n    } else {\n      try {\n        return Integer.parseInt(header);\n      } catch (NumberFormatException e) {\n        throw new IllegalArgumentException(\"Could not parse pagination number\", e);\n      }\n    }\n  }\n\n  public Set<GsonGroup> getGroups(String gitlabUrl, String token) {\n    return Set.copyOf(executePaginatedQuery(gitlabUrl, token, \"/groups\", resp -> parseJsonList(resp, GITLAB_GROUP)));\n  }\n\n  public Set<GsonUser> getDirectGroupMembers(String gitlabUrl, String token, String groupId) {\n    return getMembers(gitlabUrl, token, format(GITLAB_GROUPS_MEMBERS_ENDPOINT, groupId));\n  }\n\n  public Set<GsonUser> getAllGroupMembers(String gitlabUrl, String token, String groupId) {","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-alm-client/src/main/java/org/sonar/alm/client/gitlab/GitlabApplicationClient.java#L397-L433","documentation":"parseAndGetIntegerHeader reads the X-Page / X-Per-Page / X-Total pagination headers from a GitLab search response. If the header is absent (null), it throws this IllegalArgumentException because GitLab pagination data is mandatory for the paginated search logic. Its absence means the response did not come from a genuine GitLab projects search endpoint.","triggerScenarios":"searchProjects receives a 2xx response whose headers lack X-Page, X-Per-Page, or X-Total — typically because the response was produced by something other than the GitLab API (proxy, wrong URL), or a GitLab version/endpoint not returning these headers.","commonSituations":"Response served by a proxy or CDN that strips custom headers; pointing at a non-GitLab server that returns 200 without pagination headers; older or unusual GitLab deployments omitting X-Total on some endpoints.","solutions":["Confirm the GitLab URL points at a genuine GitLab API endpoint that returns X-Page/X-Per-Page/X-Total headers.","Check proxies/gateways for header filtering and whitelist X-Page, X-Per-Page, X-Total.","Test with curl -i against the same endpoint to inspect returned headers.","Upgrade/verify the GitLab instance version supports pagination headers on /projects."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// verify endpoint returns pagination headers before paginating\nMap<String,List<String>> h = response.headers().toMultimap();\nboolean ok = h.containsKey(\"x-page\") && h.containsKey(\"x-per-page\");","typeGuard":null,"tryCatchPattern":"try { ProjectList l = client.searchProjects(...); } catch (IllegalArgumentException e) { /* missing X-Page/X-Total: endpoint or proxy stripped headers */ }","preventionTips":["Whitelist X-Page/X-Per-Page/X-Total headers in proxies and CDNs","Verify with curl -i that the endpoint returns pagination headers","Confirm the URL points at real GitLab API, not a mimicking server"],"tags":["gitlab","pagination","http-headers","response-validation"],"backgroundTag":"missing-required-config-field","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"}