{"record":{"id":"4b568f1024e1b0e0","repo":"apache/shenyu","slug":"retry-limit-exceeded","errorCode":null,"errorMessage":"Retry limit exceeded","messagePattern":"Retry limit exceeded","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"shenyu-plugin/shenyu-plugin-httpclient/src/main/java/org/apache/shenyu/plugin/httpclient/ExponentialRetryBackoffStrategy.java","lineNumber":68,"sourceCode":"     */\n    public Mono<R> execute(final Mono<R> response, final ServerWebExchange exchange, final Duration duration, final int retryTimes) {\n        RetryBackoffSpec retrySpec = initDefaultBackoff(retryTimes);\n        return response.retryWhen(retrySpec)\n                .timeout(duration, Mono.error(() -> new java.util.concurrent.TimeoutException(\"Response took longer than timeout: \" + duration)))\n                .doOnError(e -> LOG.error(e.getMessage(), e));\n    }\n\n    private RetryBackoffSpec initDefaultBackoff(final int retryTimes) {\n        return Retry.backoff(retryTimes, Duration.ofMillis(500))\n                .maxBackoff(Duration.ofSeconds(5))\n                // Retry only for instantaneous errors\n                .transientErrors(true)\n                // Add 50% random jitter to the delay time of each retry\n                .jitter(0.5d)\n                .filter(t -> t instanceof IllegalStateException)\n                // When the maximum number of retrys is reached, a specified exception is thrown\n                .onRetryExhaustedThrow((retryBackoffSpecErr, retrySignal) -> {\n                    throw new IllegalStateException(\"Retry limit exceeded\");\n                });\n    }\n}\n","sourceCodeStart":50,"sourceCodeEnd":72,"githubUrl":"https://github.com/apache/shenyu/blob/567142e07261b3e615ae8850b30f4421f455cc5d/shenyu-plugin/shenyu-plugin-httpclient/src/main/java/org/apache/shenyu/plugin/httpclient/ExponentialRetryBackoffStrategy.java#L50-L72","documentation":"ExponentialRetryBackoffStrategy builds its Reactor retry spec (initDefaultBackoff, via retrySpec) filtering only IllegalStateException causes. When retries are exhausted it throws IllegalStateException(\"Retry limit exceeded\"), signaling the caller that the operation failed after the maximum number of retries.","triggerScenarios":"An upstream call repeatedly fails with IllegalStateException (typically 'connection prematurely closed before response' from Reactor Netty) across all retry attempts, so onRetryExhaustedThrow fires.","commonSituations":"Unstable connections to backend (premature closes), pool exhaustion causing repeated IllegalState failures, backend dropping keep-alive connections, retry max too low for a flaky network.","solutions":["Investigate the underlying IllegalStateException cause (often 'connection prematurely closed') — check backend connection handling and keep-alive settings.","Increase the max retry count in the retry strategy configuration.","Tune Netty connection-pool settings (maxConnections, pendingAcquire) to avoid pool-related failures.","Handle the resulting error in the plugin fallback path so clients get a graceful response."],"exampleFix":"// before\nretry: {attempts: 1}\n// after\nretry: {attempts: 3, backoff: \"exponential\"}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return retrySpec.execute(callable);\n} catch (IllegalStateException e) {\n  if (\"Retry limit exceeded\".equals(e.getMessage())) {\n    return fallbackResponse();\n  }\n  throw e;\n}","preventionTips":["Track 'connection prematurely closed' errors on the backend and fix keep-alive handling.","Tune Netty pool limits (maxConnections, pendingAcquire) to load.","Set retry attempts to a value that survives transient blips without hammering the upstream."],"tags":["retry","httpclient","connection"],"backgroundTag":"retry-limit-exceeded","analyzedSha":"567142e07261b3e615ae8850b30f4421f455cc5d","analyzedAt":"2026-09-12T10:08:21.293Z","contentChangedAt":"2026-09-12T10:08:21.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}