{"record":{"id":"cbcee7eb7e942309","repo":"SonarSource/sonarqube","slug":"gitlab-repository-id-is-not-numeric-s","errorCode":null,"errorMessage":"GitLab repository id is not numeric: '%s'","messagePattern":"GitLab repository id is not numeric: '(.+?)'","errorType":"exception","errorClass":"IllegalStateException","httpStatus":500,"severity":"error","filePath":"server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/ProjectBindingsServiceServerImpl.java","lineNumber":289,"sourceCode":"    AppInstallationToken accessToken = githubApplicationClient.createAppInstallationToken(githubAppConfiguration, installationId, bareRepositoryName(almRepo))\n      .orElseThrow(() -> new IllegalStateException(format(\"Failed to create a GitHub App installation token for repository '%s'\", safeAlmRepo)));\n    GithubApplicationClient.Repository repository = githubApplicationClient.getRepository(url, accessToken, almRepo)\n      .orElseThrow(() -> new IllegalStateException(format(\"Repository '%s' not found on GitHub\", safeAlmRepo)));\n    String repoUrl = requireNonNull(repository.getUrl(), format(\"GitHub returned no url for repository '%s'\", safeAlmRepo));\n    return new LiveResolution(repoUrl, Long.toString(repository.getId()));\n  }\n\n  private LiveResolution resolveGitlab(AlmSettingDto almSetting, ProjectAlmSettingDto projectAlmSetting) {\n    String pat = requireNonNull(almSetting.getDecryptedPersonalAccessToken(encryption), PAT_CANNOT_BE_NULL);\n    String url = requireNonNull(almSetting.getUrl(), URL_CANNOT_BE_NULL);\n    String almRepo = requireNonNull(projectAlmSetting.getAlmRepo(), ALM_REPO_CANNOT_BE_NULL);\n    long gitlabProjectId;\n    try {\n      gitlabProjectId = Long.parseLong(almRepo);\n    } catch (NumberFormatException e) {\n      // Long.parseLong's own exception message embeds the raw, unsanitized input — rethrow with the same\n      // sanitized-message convention used everywhere else in this class before it reaches the resolveLive log.\n      throw new IllegalStateException(format(\"GitLab repository id is not numeric: '%s'\", sanitizeForLog(almRepo)));\n    }\n    Project project = gitlabApplicationClient.getProject(url, pat, gitlabProjectId);\n    String repoUrl = requireNonNull(project.getWebUrl(), format(\"GitLab returned no web URL for project '%s'\", sanitizeForLog(almRepo)));\n    return new LiveResolution(repoUrl, String.valueOf(project.getId()));\n  }\n\n  private LiveResolution resolveAzure(AlmSettingDto almSetting, ProjectAlmSettingDto projectAlmSetting) {\n    String pat = requireNonNull(almSetting.getDecryptedPersonalAccessToken(encryption), PAT_CANNOT_BE_NULL);\n    String url = requireNonNull(almSetting.getUrl(), URL_CANNOT_BE_NULL);\n    String almSlug = requireNonNull(projectAlmSetting.getAlmSlug(), ALM_SLUG_CANNOT_BE_NULL);\n    String almRepo = requireNonNull(projectAlmSetting.getAlmRepo(), ALM_REPO_CANNOT_BE_NULL);\n    GsonAzureRepo repository = azureDevOpsHttpClient.getRepo(url, pat, almSlug, almRepo);\n    String safeAlmRepo = sanitizeForLog(almRepo);\n    String repoUrl = requireNonNull(repository.getWebUrl(), format(\"Azure DevOps returned no web URL for repository '%s'\", safeAlmRepo));\n    String repoId = requireNonNull(repository.getId(), format(\"Azure DevOps returned no id for repository '%s'\", safeAlmRepo));\n    return new LiveResolution(repoUrl, repoId);\n  }\n","sourceCodeStart":271,"sourceCodeEnd":307,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/ProjectBindingsServiceServerImpl.java#L271-L307","documentation":"IllegalStateException from ProjectBindingsServiceServerImpl.resolveGitLab: the almpeRepo parameter for a GitLab binding could not be parsed as a numeric GitLab project ID. GitLab bindings must reference the repository by its numeric ID; non-numeric values (paths, URLs, slugs) are rejected with a sanitized message.","triggerScenarios":"A resolution call (resolveLive) passes almRepo such as 'group/project' or a full URL instead of the numeric GitLab project id.","commonSituations":"Users paste the project URL or slug into a field expecting the numeric ID; tooling stores web URLs instead of project IDs; API migrations changing identifier conventions.","solutions":["Convert the GitLab project path to its numeric ID (GitLab UI > project, or GET /api/v4/projects/<url-encoded-path> returns id)","Pass only the numeric id as the repository parameter","Fix the calling integration to store the id returned at binding time"],"exampleFix":"// before\nresolveGitlab(url, pat, \"my-group/my-project\")\n// after\nconst {id} = await gitlab.get('/api/v4/projects/my-group%2Fmy-project');\nresolveGitlab(url, pat, String(id));","handlingStrategy":"validation","validationCode":"function isNumericId(v) { return /^\\d+$/.test(String(v).trim()); }\nif (!isNumericId(gitlabRepo)) {\n  throw new Error(`GitLab repo must be a numeric project id, got: ${gitlabRepo}`);\n}","typeGuard":"const isGitlabProjectId = (v) => typeof v === 'string' && /^\\d+$/.test(v);","tryCatchPattern":"try {\n  await resolver.resolveLive(params);\n} catch (e) {\n  if (e instanceof IllegalStateException && /not numeric/.test(e.message)) {\n    throw new Error('Convert the GitLab path/URL to its numeric project id first');\n  }\n  throw e;\n}","preventionTips":["Store numeric project IDs, not slugs or web URLs, at binding time","Validate input with /^\\d+$/ before calling binding APIs","Resolve paths via GET /api/v4/projects/<encoded-path> to obtain the id when only a path is known"],"tags":["sonarqube","gitlab","invalid-identifier","binding"],"backgroundTag":"invalid-identifier-format","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"}