{"record":{"id":"fea1463b40c0c462","repo":"alibaba/spring-ai-alibaba","slug":"model-call-failed-maximum-number-of-retries-reach-fea146","errorCode":null,"errorMessage":"Model call failed, maximum number of retries reached. ${maxAttempts}","messagePattern":"Model call failed, maximum number of retries reached\\. (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/modelretry/ModelRetryInterceptor.java","lineNumber":160,"sourceCode":"\n\t\t\t\t// Wait and try again\n\t\t\t\tif (currentDelay > 0) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlog.info(\"Retry after {} ms\", currentDelay);\n\t\t\t\t\t\tThread.sleep(currentDelay);\n\t\t\t\t\t} catch (InterruptedException ie) {\n\t\t\t\t\t\tThread.currentThread().interrupt();\n\t\t\t\t\t\tthrow new RuntimeException(\"Retry interrupted\", ie);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Calculate the next delay time (exponential backoff)\n\t\t\t\tcurrentDelay = Math.min((long) (currentDelay * backoffMultiplier), maxDelay);\n\t\t\t}\n\t\t}\n\n\t\t// All retries failed.\n\t\tthrow new RuntimeException(\"Model call failed, maximum number of retries reached. \" + maxAttempts, lastException);\n\t}\n\n\tprivate Flux<ChatResponse> withStreamingRetry(ModelRequest request, ModelCallHandler handler, Flux<ChatResponse> responseFlux,\n\t\t\tint attempt, long currentDelay) {\n\t\treturn Flux.defer(() -> {\n\t\t\tAtomicBoolean chunkEmitted = new AtomicBoolean(false);\n\t\t\treturn responseFlux.doOnNext(response -> chunkEmitted.set(true)).onErrorResume(error -> {\n\t\t\t\tif (chunkEmitted.get()) {\n\t\t\t\t\t// Retrying after partial output would duplicate chunks downstream.\n\t\t\t\t\treturn Flux.error(error);\n\t\t\t\t}\n\t\t\t\treturn retryStreamingModelCall(request, handler, attempt, currentDelay, error);\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate Flux<ChatResponse> retryStreamingModelCall(ModelRequest request, ModelCallHandler handler, int failedAttempt,\n\t\t\tlong currentDelay, Throwable error) {","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/modelretry/ModelRetryInterceptor.java#L142-L178","documentation":"Defensive terminal throw after ModelRetryInterceptor's for-loop over attempts completes without returning — all maxAttempts attempts failed with retryable errors. It throws RuntimeException(\"Model call failed, maximum number of retries reached. \" + maxAttempts) with the last exception as cause. (In practice usually unreachable because line 132-134 throws earlier on the final attempt.)","triggerScenarios":"interceptModel where every attempt throws a retryable-predicate-passing exception and control flow exits the loop — logically equivalent to error 614 but reachable only if the final attempt's failure bypasses the earlier maxAttempts check.","commonSituations":"Sustained provider unavailability across the whole retry window; all attempts failing with retryable network errors (connection reset, timeout); retry budget too small relative to the outage duration.","solutions":["Inspect the cause for the true final failure and check provider health/credentials.","Increase maxAttempts, initialDelay, and maxDelay to ride out longer outages.","Wrap with ModelFallbackInterceptor for cross-provider resilience.","Add logging/metrics on retry exhaustion to alert before clients see this error."],"exampleFix":"// before\nModelRetryInterceptor.builder().maxAttempts(2).initialDelay(500).build();\n// after\nModelRetryInterceptor.builder().maxAttempts(5).initialDelay(1000).maxDelay(15000).backoffMultiplier(2.0).build();","handlingStrategy":"retry","validationCode":"// Ensure sane retry budget before running\nModelRetryInterceptor.Builder b = ModelRetryInterceptor.builder();\nassert b != null; // configure maxAttempts>=3, maxDelay>=10s for production traffic","typeGuard":null,"tryCatchPattern":"try {\n    return retryInterceptor.interceptModel(request, handler);\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Model call failed, maximum number of retries reached. \")) {\n        log.error(\"Retry budget exhausted ({} attempts); final cause: {}\", e.getMessage(), e.getCause());\n        return fallbackHandler.apply(request); // circuit-break / degrade\n    }\n    throw e;\n}","preventionTips":["Use exponential backoff with a maxDelay high enough to ride out short outages.","Add a circuit breaker so a down provider fails fast instead of burning the full retry budget on every request.","Track retry-exhaustion rates in metrics/dashboards."],"tags":["retry-exhausted","backoff","llm","network"],"backgroundTag":"retry-limit-exceeded","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}