SonarSource/sonarqube · error · ForbiddenException

Insufficient privileges

Error message

Insufficient privileges

What it means

The api/measures/component endpoint requires the caller to have at least one of: User (Browse) permission on the component, Scan (Execute Analysis) permission on the component, or global Execute Analysis permission. checkPermissions throws 'Insufficient privileges' when none of the three holds, hiding whether the component exists.

Solutions

  1. Grant the user Browse (User) permission on the component/project, or include the component in a group with Browse.
  2. Alternatively grant global 'Execute Analysis' permission if the caller is a scanning account.
  3. Check you are querying the right branch/component key; access is per-component, so a valid key on an unauthorized project still fails.
  4. Confirm the token is not expired or belonging to a deactivated user.

Example fix

// before
curl -u "$TOKEN:" "$SONAR/api/measures/component?component=com.acme:app&metricKeys=ncloc"
// after: grant 'Browse' to the token's user on com.acme:app, or use an account with global Execute Analysis
Defensive patterns

Strategy: validation

Validate before calling

const me = await get('/api/authentication/validate', { auth: token });
const perms = await get(`/api/permissions/authorization?projectKey=${encodeURIComponent(component)}`, { auth: token });
if (!perms.permissions.includes('user') && !perms.permissions.includes('scan') && !perms.globalPermissions.includes('scan')) {
  throw new Error(`No Browse/Scan permission on ${component}`);
}

Type guard

function canReadComponent(authz) {
  return Boolean(authz && (authz.permissions.includes('user') || authz.permissions.includes('scan') || (authz.globalPermissions || []).includes('scan')));
}

Try / catch

try {
  return await getMeasures(component);
} catch (e) {
  if (e.response && e.response.status === 403) return null; // skip inaccessible components in dashboards
  throw e;
}

Prevention

When it happens

Trigger: GET api/measures/component with component/branch keys referencing a project the user cannot browse and has no scan permission; anonymous request on a server where anonymous access is disabled.

Common situations: CI token (scan-only, no browse) querying measures of another team's project without global scan; dashboards using a personal token that lost project access after group membership changes; measures pulled for a branch the user cannot see.

Understand the failure class

Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentAction.java:298

    }
  }

  private static ComponentRequest toComponentWsRequest(Request request) {
    ComponentRequest componentRequest = new ComponentRequest()
      .setComponent(request.mandatoryParam(PARAM_COMPONENT))
      .setBranch(request.param(PARAM_BRANCH))
      .setPullRequest(request.param(PARAM_PULL_REQUEST))
      .setAdditionalFields(request.paramAsStrings(PARAM_ADDITIONAL_FIELDS))
      .setMetricKeys(request.mandatoryParamAsStrings(PARAM_METRIC_KEYS));
    checkRequest(!componentRequest.getMetricKeys().isEmpty(), "At least one metric key must be provided");
    return componentRequest;
  }

  private void checkPermissions(ComponentDto baseComponent) {
    if (!userSession.hasComponentPermission(ProjectPermission.USER, baseComponent) &&
      !userSession.hasComponentPermission(ProjectPermission.SCAN, baseComponent) &&
      !userSession.hasPermission(GlobalPermission.SCAN)) {
      throw insufficientPrivilegesException();
    }
  }

  private static class ComponentRequest {
    private String component = null;
    private String branch = null;
    private String pullRequest = null;
    private List<String> metricKeys = null;
    private List<String> additionalFields = null;

    private String getComponent() {
      return component;
    }

    private ComponentRequest setComponent(@Nullable String component) {
      this.component = component;
      return this;
    }

View on GitHub (pinned to 184c821202)