{"record":{"id":"43a2cc80d6f5a079","repo":"SonarSource/sonarqube","slug":"the-bitbucket-project-in-which-the-repository-s","errorCode":null,"errorMessage":"The BitBucket project, in which the repository %s is located, is mandatory","messagePattern":"The BitBucket project, in which the repository (.+?) is located, is mandatory","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreator.java","lineNumber":111,"sourceCode":"\n    ComponentCreationData componentCreationData = projectCreator.getOrCreateProject(dbSession, request);\n    ProjectDto projectDto = Optional.ofNullable(componentCreationData.projectDto()).orElseThrow();\n    createProjectAlmSettingDto(dbSession, repository, projectDto, almSettingDto, monorepo);\n\n    return componentCreationData;\n  }\n\n  private String findPersonalAccessTokenOrThrow(DbSession dbSession) {\n    String userUuid = requireNonNull(userSession.getUuid(), \"User UUID cannot be null.\");\n    Optional<AlmPatDto> almPatDto = dbClient.almPatDao().selectByUserAndAlmSetting(dbSession, userUuid, almSettingDto);\n\n    return almPatDto.map(AlmPatDto::getPersonalAccessToken)\n      .orElseThrow(() -> new IllegalArgumentException(format(\"personal access token for '%s' is missing\", almSettingDto.getKey())));\n  }\n\n  private String getBitbucketProjectOrThrow() {\n    if (devOpsProjectDescriptor.projectIdentifier() == null) {\n      throw new IllegalArgumentException(String.format(\"The BitBucket project, in which the repository %s is located, is mandatory\",\n        devOpsProjectDescriptor.repositoryIdentifier()));\n    }\n    return devOpsProjectDescriptor.projectIdentifier();\n  }\n\n  private String getDefaultBranchName(String url, String pat, String project, String repo) {\n    BranchesList branches = bitbucketServerRestClient.getBranches(url, pat, project, repo);\n    Optional<Branch> defaultBranch = branches.findDefaultBranch();\n    return defaultBranch.map(Branch::getName).orElse(null);\n  }\n\n  private String getProjectKey(@Nullable String projectKey, Repository repo) {\n    return Optional.ofNullable(projectKey).orElseGet(() -> projectKeyGenerator.generateUniqueProjectKey(repo.getProject().getKey(), repo.getSlug()));\n  }\n\n  private static String getProjectName(@Nullable String projectName, Repository repository) {\n    return Optional.ofNullable(projectName).orElse(repository.getName());\n  }","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/bitbucketserver/BitbucketServerProjectCreator.java#L93-L129","documentation":"BitbucketServerProjectCreator.getBitbucketProjectOrThrow() requires devOpsProjectDescriptor.projectIdentifier() (the Bitbucket project key) to be non-null before creating/importing the repository. When the descriptor was built without a project key, it throws IllegalArgumentException naming the repository identifier.","triggerScenarios":"Invoking Bitbucket Server project creation/binding with a DevOpsProjectDescriptor whose projectIdentifier is null — e.g. request parameters include repository but omit the Bitbucket project, or the descriptor was constructed programmatically without it.","commonSituations":"API calls to create the ALM binding omitting the 'project' parameter; automation scripts parsing only repo slug from a Bitbucket URL and dropping the project part.","solutions":["Pass the Bitbucket project key explicitly in the create/bind request alongside the repository","Derive the project from the full repository URL (https://bitbucket.example.com/projects/<PROJ>/repos/<repo>) instead of just the repo name","Fix the code constructing DevOpsProjectDescriptor to populate projectIdentifier before calling the creator","Validate inputs client-side before submitting the descriptor"],"exampleFix":"// before\nnew DevOpsProjectDescriptor(..., projectIdentifier=null, repositoryIdentifier=\"my-repo\")\n// after\nnew DevOpsProjectDescriptor(..., projectIdentifier=\"PROJ\", repositoryIdentifier=\"my-repo\")","handlingStrategy":"validation","validationCode":"// require both project key and repo before invoking the creator\nif (!descriptor.projectIdentifier()) {\n  throw new Error('Bitbucket project key is required along with repository');\n}\nconst m = /\\/projects\\/([^/]+)\\/repos\\/([^/]+)/.exec(repoUrl);\nconst projectKey = m?.[1], repository = m?.[2];","typeGuard":"// Kotlin/Java-style null guard\nfun hasProject(d: DevOpsProjectDescriptor) = !d.projectIdentifier().isNullOrEmpty()","tryCatchPattern":"try {\n  creator.create(...);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"is mandatory\")) {\n    // supply the Bitbucket project key and retry\n  } else throw e;\n}","preventionTips":["Always derive the Bitbucket project key from the full repository URL","Validate DevOpsProjectDescriptor fields for null before construction/submission","Document that Bitbucket Server bindings require both project and repository","Add request-level validation before hitting the creator service"],"tags":["bitbucket","validation","missing-argument"],"backgroundTag":"missing-required-argument","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}