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

  1. Read the exceptionText after the colon in the message — it names the underlying provider/network error; fix that root cause.
  2. Increase maxAttempts and/or delays via the builder so transient outages have time to clear.
  3. Verify provider connectivity out-of-band (curl the endpoint) and check API key/quota status.
  4. 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

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


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/c2fd3270e268341d. Report an issue: GitHub.