SonarSource/sonarqube · error · IllegalArgumentException

Provided user with login '%s' does not have 'Browse' permiss

Error message

Provided user with login '%s' does not have 'Browse' permission to project

What it means

Thrown by the assign action of the Security Hotspots web service when the user chosen as assignee cannot see the project the hotspot belongs to. For private projects, SonarQube requires that any assignable user holds the 'Browse' (USER) permission on the project. The server rejects the assignment rather than silently granting visibility.

Source

Thrown at server/sonar-webserver-webapi/src/main/java/org/sonar/server/hotspot/ws/AssignAction.java:158

    }
  }

  private static void checkHotspotStatusAndResolution(IssueDto hotspotDto) {
    if (!STATUS_TO_REVIEW.equals(hotspotDto.getStatus()) && !RESOLUTION_ACKNOWLEDGED.equals(hotspotDto.getResolution())) {
      throw new IllegalArgumentException("Cannot change the assignee of this hotspot given its current status and resolution");
    }
  }

  private UserDto getAssignee(DbSession dbSession, String assignee) {
    return checkFound(dbClient.userDao().selectActiveUserByLogin(dbSession, assignee), "Unknown user: %s", assignee);
  }

  private void checkAssigneeProjectPermission(DbSession dbSession, UserDto assignee, String issueBranchUuid) {
    ProjectDto project = checkFoundWithOptional(dbClient.projectDao().selectByBranchUuid(dbSession, issueBranchUuid),
      "Could not find branch for issue");

    if (project.isPrivate() && !hasProjectPermission(dbSession, assignee.getUuid(), project.getUuid())) {
      throw new IllegalArgumentException(String.format("Provided user with login '%s' does not have 'Browse' permission to project", assignee.getLogin()));
    }
  }

  private boolean hasProjectPermission(DbSession dbSession, String userUuid, String projectUuid) {
    return dbClient.authorizationDao().selectEntityPermissions(dbSession, projectUuid, userUuid).contains(ProjectPermission.USER.getKey());
  }

  private static HotspotChangedEvent buildEventData(DefaultIssue defaultIssue, @Nullable UserDto assignee, String filePath) {
    return new HotspotChangedEvent.Builder()
      .setKey(defaultIssue.key())
      .setProjectKey(defaultIssue.projectKey())
      .setStatus(defaultIssue.status())
      .setResolution(defaultIssue.resolution())
      .setUpdateDate(defaultIssue.updateDate())
      .setAssignee(assignee == null ? null : assignee.getLogin())
      .setFilePath(filePath)
      .build();
  }

View on GitHub (pinned to 184c821202)

Solutions

  1. Grant the assignee the 'User' (Browse) permission on the project in Project Settings > Permissions
  2. Check the user has access via GET api/permissions/users with projectKey before assigning
  3. Assign to a different user who already has Browse permission
  4. If the project should be public, change visibility in Project Settings so the check is skipped

Example fix

// before
curl -X POST '.../api/hotspots/assign?hotspot=AX1&assign=ci-bot'
// after
# grant permission first, then assign
curl -X POST '.../api/permissions/add_user?projectKey=my_project&login=ci-bot&permission=user'
curl -X POST '.../api/hotspots/assign?hotspot=AX1&assign=ci-bot'
Defensive patterns

Strategy: validation

Validate before calling

const perms = await get('/api/permissions/users', {projectKey, login: assigneeLogin, permission: 'user'});
if (!perms.permissions.some(p => p.login === assigneeLogin)) throw new Error('assignee lacks Browse permission');

Try / catch

try { await post('/api/hotspots/assign', {hotspot, assign: login}); } catch (e) { if (e.status === 400 && e.message.includes("does not have 'Browse'")) { /* grant permission or pick another user */ } else throw e; }

Prevention

When it happens

Trigger: Calling POST api/hotspots/assign with an 'assign' login whose user is not in the project's USER permission list while the project is private. The check only runs for private projects, so public projects never hit it.

Common situations: Assigning a hotspot to an admin or bot account that was never granted project access; assigning to a user of another organization; project visibility switched to private after users were assigned previously; automated scripts using a service account without Browse permission.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/f4bf00ee137cd48a. Report an issue: GitHub.