SonarSource/sonarqube · warning · ServerException

The maximum number of concurrent calls for this web service…

Error message

The maximum number of concurrent calls for this web service has been reached

What it means

ConcurrentCallsLimitInterceptor.preAction enforces the @MaxConcurrentCalls-permit limit declared on a web service action. When all permits of the action's semaphore are held, it throws ServerException 503 telling the client the web service has reached its concurrency cap.

Solutions

  1. Retry the request after a delay with backoff; 503 here is transient once other calls finish.
  2. Reduce client-side concurrency (limit worker/parallelism count) below the server limit.
  3. Investigate why in-flight calls are slow and holding permits (DB, ES, downstream latency).
  4. If the limit is too low for legitimate traffic, raise the @MaxConcurrentCalls value on the server action.

Example fix

// before
for (Project p : projects) executor.submit(() -> callSlowApi(p)); // 100 parallel
// after
ExecutorService pool = Executors.newFixedThreadPool(5); // stay under the limit
for (Project p : projects) pool.submit(() -> retryOn503(() -> callSlowApi(p)));
Defensive patterns

Strategy: retry

Try / catch

if (response.code() == 503 && response.message().contains("concurrent calls")) { Thread.sleep(backoff); retry(); }

Prevention

When it happens

Trigger: Sending more simultaneous requests than the action's annotation.value() permits to an endpoint annotated with a concurrency limit (key = action.path() + '/' + action.key()) while existing calls are still in flight.

Common situations: Load tests or parallel CI scans hammering a rate-limited endpoint; a slow downstream dependency causing calls to pile up and exhaust the semaphore; burst retries after a timeout.

Related errors


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

Appendix: source

Thrown at server/sonar-webserver-core/src/main/java/org/sonar/server/platform/web/ConcurrentCallsLimitInterceptor.java:50

 * When the maximum number of concurrent calls is reached, returns HTTP 503.
 */
public class ConcurrentCallsLimitInterceptor implements ActionInterceptor {

  private final ConcurrentHashMap<String, Semaphore> semaphores = new ConcurrentHashMap<>();
  private final ThreadLocal<Semaphore> acquiredSemaphore = new ThreadLocal<>();

  @Override
  public void preAction(WebService.Action action, Request request) {
    ConcurrentCallsLimit annotation = action.handler().getClass().getAnnotation(ConcurrentCallsLimit.class);
    if (annotation == null) {
      return;
    }
    String key = action.path() + "/" + action.key();
    Semaphore semaphore = semaphores.computeIfAbsent(key, k -> new Semaphore(annotation.value()));
    if (semaphore.tryAcquire()) {
      acquiredSemaphore.set(semaphore);
    } else {
      throw new ServerException(503, "The maximum number of concurrent calls for this web service has been reached");
    }
  }

  @Override
  public void postAction(WebService.Action action, Request request) {
    Semaphore semaphore = acquiredSemaphore.get();
    if (semaphore != null) {
      semaphore.release();
      acquiredSemaphore.remove();
    }
  }
}

View on GitHub (pinned to 184c821202)