SonarSource/sonarqube · error · ForbiddenException

Insufficient privileges

Error message

Insufficient privileges

What it means

SafeModeMonitoringMetricAction exposes Prometheus-format monitoring metrics in safe mode. Access requires one of: the system passcode, system administrator authentication, or the monitoring bearer passcode; if none validates, it throws ForbiddenException 'Insufficient privileges'.

Source

Thrown at server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/SafeModeMonitoringMetricAction.java:72

  public void define(WebService.NewController context) {
    context.createAction("metrics")
      .setSince("9.3")
      .setDescription("""
        Return monitoring metrics in Prometheus format. \s
        Support content type 'text/plain' (default) and 'application/openmetrics-text'.
        This endpoint can be accessed using a Bearer token, which needs to be defined in sonar.properties with the 'sonar.web.systemPasscode' key.""")
      .setChangelog(
        new Change("2026.3", "Added 'sonarqube_elasticsearch_read_only_indices_total' and 'sonarqube_elasticsearch_disk_usage_percent' metrics"))
      .setResponseExample(Resources.getResource(SafeModeMonitoringMetricAction.class, "monitoring-metrics.txt"))
      .setHandler(this);
    isWebUpGauge.set(1D);
  }

  @Override
  public void handle(Request request, Response response) throws Exception {

    if (!systemPasscode.isValid(request) && !isSystemAdmin() && !bearerPasscode.isValid(request)) {
      throw new ForbiddenException("Insufficient privileges");
    }

    String requestContentType = request.header("accept").orElse(null);
    String contentType = TextFormat.chooseContentType(requestContentType);

    response.setHeader(HttpHeaders.CONTENT_TYPE, contentType);
    response.stream().setStatus(200);

    try (Writer writer = new OutputStreamWriter(response.stream().output(), UTF_8)) {
      TextFormat.writeFormat(contentType, writer, CollectorRegistry.defaultRegistry.metricFamilySamples());
      writer.flush();
    }
  }

  public boolean isSystemAdmin() {
    // No authenticated user in safe mode
    return false;
  }

View on GitHub (pinned to 184c821202)

Solutions

  1. Add the correct bearer token (sonar.monitoringPasscode value) to the Prometheus scrape config's authorization credentials.
  2. Alternatively pass the system passcode via X-Sonar-Passcode, or authenticate the scrape as a system administrator.
  3. Confirm the passcode properties on the server match the scraper secret and reload the scraper after rotation.

Example fix

// before
scrape_configs: [{ job_name: 'sonarqube', static_configs: [{ targets: ['sonarqube:9000'] }] }]
// after
scrape_configs: [{ job_name: 'sonarqube', static_configs: [{ targets: ['sonarqube:9000'] }], authorization: { credentials: '${SONAR_MONITORING_PASSCODE}' } }]
Defensive patterns

Strategy: validation

Validate before calling

const auth = monitoringPasscode ?? systemPasscode ?? adminToken;
if (!auth) throw new Error('no valid credential available for metrics endpoint');

Try / catch

try {
  await scrapeMetrics({ authorization: `Bearer ${monitoringPasscode}` });
} catch (e) {
  if (e.status === 403) reloadPasscodeAndRetryOnce();
  else throw e;
}

Prevention

When it happens

Trigger: Scraping the safe-mode metrics endpoint without X-Sonar-Passcode, without admin user credentials, and without a valid sonar.monitoringPasscode bearer token — e.g. Prometheus scrape config missing the authorization header.

Common situations: Prometheus/Grafana setups where sonar.monitoringPasscode was introduced or rotated but the scrape job's bearer_token was not updated; metrics scraped from a clustered node in safe mode with only anonymous access.

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/bd8df405d525abc8. Report an issue: GitHub.