OpenFeign/feign · error · ClientException

SERVER_THROTTLED

SERVER_THROTTLED

Error message

SERVER_THROTTLED

What it means

LBClient (ribbon module) executes the request through the underlying Feign client and, if the HTTP response status is one of the configured retryableStatusCodes, closes the response and throws a ribbon ClientException with ErrorType.SERVER_THROTTLED. This signals the server throttled/rejected the call so ribbon's load balancer can retry on another server.

Solutions

  1. Retry the call (ribbon does this automatically if the status is in the retry handler); add backoff/jitter for repeated throttle responses
  2. Reduce request rate or add client-side rate limiting / circuit breaking
  3. Adjust retryableStatusCodes in LBClient.Factory if the status codes your server uses for throttling differ
  4. Scale or fix the upstream server returning the throttle status
Defensive patterns

Strategy: retry

Try / catch

try {
  return lbClient.execute(request, options);
} catch (com.netflix.client.ClientException e) {
  if (e.getErrorType() == ClientException.ErrorType.SERVER_THROTTLED) {
    // backoff and retry / fail over to another server
  }
  throw e;
}

Prevention

When it happens

Trigger: The remote server responds with an HTTP status contained in retryableStatusCodes (typically 503 or similar throttle statuses) during LBClient.execute().

Common situations: Server-side rate limiting or overload returning 503/429; an upstream behind a load balancer shedding load; misconfigured retryable status list that includes statuses the server uses for other purposes.

Related errors


AI-assisted analysis of OpenFeign/feign@e2a1e27560 (2026-09-10). Data as JSON: /api/errors/63867ecfd7272336. Report an issue: GitHub.

Appendix: source

Thrown at ribbon/src/main/java/feign/ribbon/LBClient.java:94

      throws IOException, ClientException {
    Request.Options options;
    if (configOverride != null) {
      options =
          new Request.Options(
              configOverride.get(CommonClientConfigKey.ConnectTimeout, connectTimeout),
              TimeUnit.MILLISECONDS,
              (configOverride.get(CommonClientConfigKey.ReadTimeout, readTimeout)),
              TimeUnit.MILLISECONDS,
              configOverride.get(CommonClientConfigKey.FollowRedirects, followRedirects));
    } else {
      options =
          new Request.Options(
              connectTimeout, TimeUnit.MILLISECONDS, readTimeout, TimeUnit.MILLISECONDS, true);
    }
    Response response = request.client().execute(request.toRequest(), options);
    if (retryableStatusCodes.contains(response.status())) {
      response.close();
      throw new ClientException(ClientException.ErrorType.SERVER_THROTTLED);
    }
    return new RibbonResponse(request.getUri(), response);
  }

  @Override
  public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
      RibbonRequest request, IClientConfig requestConfig) {
    if (clientConfig.get(CommonClientConfigKey.OkToRetryOnAllOperations, false)) {
      return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
    }
    if (request.toRequest().httpMethod() != HttpMethod.GET) {
      return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(), requestConfig);
    } else {
      return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
    }
  }

  static class RibbonRequest extends ClientRequest implements Cloneable {

View on GitHub (pinned to e2a1e27560)