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
- Retry the call (ribbon does this automatically if the status is in the retry handler); add backoff/jitter for repeated throttle responses
- Reduce request rate or add client-side rate limiting / circuit breaking
- Adjust retryableStatusCodes in LBClient.Factory if the status codes your server uses for throttling differ
- 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
- Add client-side rate limiting and exponential backoff with jitter
- Monitor upstream 503/429 rates and configure retryableStatusCodes to match your server
- Use ribbon retries and circuit breakers to survive transient throttling
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)