{"record":{"id":"bd7fe4171fac783e","repo":"SonarSource/sonarqube","slug":"cannot-change-the-assignee-of-this-hotspot-given-i","errorCode":null,"errorMessage":"Cannot change the assignee of this hotspot given its current status and resolution","messagePattern":"Cannot change the assignee of this hotspot given its current status and resolution","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/AssignAction.java","lineNumber":145,"sourceCode":"      if (assignee != null) {\n        checkAssigneeProjectPermission(dbSession, assignee, hotspotDto.getProjectUuid());\n      }\n\n      if (issueFieldsSetter.assign(defaultIssue, assignee, context)) {\n        issueUpdater.saveIssueAndPreloadSearchResponseData(dbSession, hotspotDto, defaultIssue, context);\n\n        BranchDto branch = issueUpdater.getBranch(dbSession, defaultIssue);\n        if (BRANCH.equals(branch.getBranchType())) {\n          HotspotChangedEvent hotspotChangedEvent = buildEventData(defaultIssue, assignee, hotspotDto.getFilePath());\n          hotspotChangeEventService.distributeHotspotChangedEvent(branch.getProjectUuid(), hotspotChangedEvent);\n        }\n      }\n    }\n  }\n\n  private static void checkHotspotStatusAndResolution(IssueDto hotspotDto) {\n    if (!STATUS_TO_REVIEW.equals(hotspotDto.getStatus()) && !RESOLUTION_ACKNOWLEDGED.equals(hotspotDto.getResolution())) {\n      throw new IllegalArgumentException(\"Cannot change the assignee of this hotspot given its current status and resolution\");\n    }\n  }\n\n  private UserDto getAssignee(DbSession dbSession, String assignee) {\n    return checkFound(dbClient.userDao().selectActiveUserByLogin(dbSession, assignee), \"Unknown user: %s\", assignee);\n  }\n\n  private void checkAssigneeProjectPermission(DbSession dbSession, UserDto assignee, String issueBranchUuid) {\n    ProjectDto project = checkFoundWithOptional(dbClient.projectDao().selectByBranchUuid(dbSession, issueBranchUuid),\n      \"Could not find branch for issue\");\n\n    if (project.isPrivate() && !hasProjectPermission(dbSession, assignee.getUuid(), project.getUuid())) {\n      throw new IllegalArgumentException(String.format(\"Provided user with login '%s' does not have 'Browse' permission to project\", assignee.getLogin()));\n    }\n  }\n\n  private boolean hasProjectPermission(DbSession dbSession, String userUuid, String projectUuid) {\n    return dbClient.authorizationDao().selectEntityPermissions(dbSession, projectUuid, userUuid).contains(ProjectPermission.USER.getKey());","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/AssignAction.java#L127-L163","documentation":"Security hotspot assignees can only be changed while the hotspot is still 'in review'. AssignAction.checkHotspotStatusAndResolution throws this IllegalArgumentException when the hotspot's status/resolution indicates it is no longer in TO_REVIEW state (and not merely ACKNOWLEDGED), so changing the assignee is not allowed.","triggerScenarios":"Calling api/hotspots/assign on a hotspot whose status is not 'TO_REVIEW' and whose resolution is not 'ACKNOWLEDGED' — i.e. it was already marked FIXED or SAFE by a reviewer, or closed by analysis changes.","commonSituations":"Two users editing the same hotspot concurrently (one closes it, the other then tries to reassign); automation reassigning stale hotspot keys after the underlying code changed; retrying an old assign request after the hotspot was reviewed.","solutions":["Re-fetch the hotspot (api/hotspots/show) and confirm its status is TO_REVIEW before assigning.","If the hotspot was already reviewed, do not reassign; it may need to be reopened via code change/re-analysis instead.","Handle the race in automation: catch the 400 error and re-check current state before retrying."],"exampleFix":"// before\n// blindly assign without checking state\nws.post(\"api/hotspots/assign\", {hotspot: key, assignee: login});\n// after\nconst hotspot = await ws.get(\"api/hotspots/show\", {hotspot: key});\nif (hotspot.status === \"TO_REVIEW\") {\n  await ws.post(\"api/hotspots/assign\", {hotspot: key, assignee: login});\n}","handlingStrategy":"try-catch","validationCode":"const hotspot = await ws.get(\"api/hotspots/show\", {hotspot: key});\nif (hotspot.status !== \"TO_REVIEW\" && hotspot.resolution !== \"ACKNOWLEDGED\") {\n  throw new Error(\"Hotspot no longer in review; cannot reassign\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  await ws.post(\"api/hotspots/assign\", {hotspot: key, assignee: login});\n} catch (err) {\n  if (err.message.includes(\"Cannot change the assignee of this hotspot\")) {\n    const cur = await ws.get(\"api/hotspots/show\", {hotspot: key});\n    log.warn(\"Hotspot state changed to {} by someone else\", cur.status); // re-check and skip/reopen\n  } else throw err;\n}","preventionTips":["Always fetch the hotspot's current status before assigning.","Design automation to tolerate concurrent review state changes (optimistic re-check).","Refresh hotspot keys after re-analysis, as hotspots may be closed by code changes."],"tags":["java","sonarqube","web-api","security-hotspots","state"],"backgroundTag":"invalid-state-transition","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"}