alibaba/spring-ai-alibaba · error · RuntimeException
Model call failed, maximum number of retries reached:${excep
Error message
Model call failed, maximum number of retries reached:${exceptionText} What it means
ModelRetryInterceptor detected an 'Exception:...' text payload in the model response (an error captured upstream as message text) and retried it until attempt >= maxAttempts without success. It throws RuntimeException('Model call failed, maximum number of retries reached:' + exceptionText) carrying the embedded error text. Note the original exception is not a cause here — the detail is embedded in the message string.
Source
Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/modelretry/ModelRetryInterceptor.java:115
// Extract anomaly information from the text and determine whether a retry is possible.
if (attempt < maxAttempts && isRetryableExceptionMessage(exceptionText)) {
lastException = new RuntimeException(exceptionText);
// Wait and try again
if (currentDelay > 0) {
try {
log.info("Retry after {} ms", currentDelay);
Thread.sleep(currentDelay);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Retry interrupted", e);
}
}
// Calculate the next delay time (exponential backoff)
currentDelay = Math.min((long) (currentDelay * backoffMultiplier), maxDelay);
continue;
} else if (attempt >= maxAttempts) {
log.error("The maximum number of retries has been reached {}, and the model call has failed.", maxAttempts);
throw new RuntimeException("Model call failed, maximum number of retries reached:" + exceptionText);
}
// For non-retryable exceptions, return immediately.
return modelResponse;
}
// Successful response
if (attempt > 1) {
log.info("The model call succeeded after the {}th attempt.", attempt);
}
return modelResponse;
} catch (Exception e) {
lastException = e;
log.warn("Model call failed (attempted {}/{}): {}", attempt, maxAttempts, e.getMessage());
if (attempt >= maxAttempts) {
log.error("The maximum number of retries has been reached {}, and the model call has failed.", maxAttempts);View on GitHub (pinned to f82da0b50f)
Solutions
- Read the exceptionText after the colon in the message — it names the underlying provider/network error; fix that root cause.
- Increase maxAttempts and/or delays via the builder so transient outages have time to clear.
- Verify provider connectivity out-of-band (curl the endpoint) and check API key/quota status.
- If the error is not actually transient (e.g. auth failure misreported with 'connection' in the text), tighten isRetryableExceptionMessage via retryableExceptionPredicate or fix the upstream text.
Example fix
// before: too small a budget for flaky network ModelRetryInterceptor.builder().maxAttempts(3).initialDelay(1000).build(); // after ModelRetryInterceptor.builder().maxAttempts(5).initialDelay(2000).maxDelay(30000).backoffMultiplier(2.0).build();
Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight connectivity check before the call
try (var s = new Socket()) { s.connect(new InetSocketAddress(host, 443), 3000); }
catch (IOException e) { throw new IllegalStateException("Provider endpoint unreachable: " + host, e); } Try / catch
try {
return retryInterceptor.interceptModel(request, handler);
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Model call failed, maximum number of retries reached:")) {
String rootText = e.getMessage().substring(e.getMessage().indexOf(':') + 1);
log.error("Retry exhausted; embedded provider error: {}", rootText);
throw new ProviderUnavailableException(rootText, e);
}
throw e;
} Prevention
- Size maxAttempts/delays to exceed your provider's typical transient-outage duration.
- Alert on retry-rate metrics rather than waiting for exhaustion errors.
- Pair with ModelFallbackInterceptor so exhaustion falls through to another provider.
When it happens
Trigger: interceptModel receiving a non-streaming response whose message text starts with 'Exception:', where isRetryableExceptionMessage(text) matched (contains connection/timeout/network/etc.), for all maxAttempts attempts. The last attempt takes the `attempt >= maxAttempts` branch at line 113-115.
Common situations: A persistent network problem to the LLM provider (proxy down, DNS failure, endpoint unreachable) that never recovers within the retry budget; retries too few/fast (maxAttempts=3 with 1s initial delay) for a longer outage; AgentLlmNode wrapping real exceptions as text so the cause chain is unavailable.
Related errors
- Model call failed, maximum number of retries reached.
- Model call failed, maximum number of retries reached. ${maxA
- Failed to get valid decision after %d retries. Last invalid
- All models failed after ${fallbackModels.size() + 1} attempt
- INVALID_PARAMS
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/c2fd3270e268341d.
Report an issue: GitHub.