alibaba/spring-ai-alibaba · error · RuntimeException

Model call failed, maximum number of retries reached.

Error message

Model call failed, maximum number of retries reached.

What it means

ModelRetryInterceptor's generic catch block: a direct exception from handler.call(request) occurred on the final allowed attempt (attempt >= maxAttempts), so it throws RuntimeException("Model call failed, maximum number of retries reached.") with the last exception preserved as the cause. The retry policy gave up after exhausting all attempts.

Solutions

  1. Inspect getCause() of this RuntimeException — it holds the final underlying model-call exception; fix that root problem.
  2. Check provider status page and your API key validity/quota; 401/429 errors will fail every attempt.
  3. Raise maxAttempts or lengthen backoff if failures are transient bursts (rate limit spikes).
  4. Combine with ModelFallbackInterceptor so a different model is tried after retries are exhausted.

Example fix

// before: retries then hard failure
ModelRetryInterceptor.builder().maxAttempts(3).build();
// after: retry then fall back to another provider
var retry = ModelRetryInterceptor.builder().maxAttempts(3).build();
var fallback = ModelFallbackInterceptor.builder().addFallbackModel(otherProviderModel).build();
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate configuration before calling the model
if (apiKey == null || apiKey.isBlank()) throw new IllegalStateException("Missing provider API key");
if (modelName == null || !allowedModels.contains(modelName)) throw new IllegalStateException("Unknown model: " + modelName);

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.")) {
        Throwable root = e.getCause();
        log.error("All {} attempts failed; final cause: {}", maxAttempts, root, root);
        throw new ModelCallException(root); // typed wrapper for callers
    }
    throw e;
}

Prevention

When it happens

Trigger: interceptModel where handler.call throws on every one of maxAttempts attempts (e.g. persistent HTTP 4xx/5xx, timeout, I/O error); on the final attempt the `attempt >= maxAttempts` check fires before the retryability predicate is consulted.

Common situations: Provider outage or sustained rate limiting outlasting the retry window; wrong API key causing every call to fail; model name not found on the target endpoint; local network/firewall blocking the provider consistently.

Related errors


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

Appendix: source

Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/modelretry/ModelRetryInterceptor.java:134

					}

					// 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);
					throw new RuntimeException("Model call failed, maximum number of retries reached.", lastException);
				}

				// Determine if an exception can be retried.
				if (!retryableExceptionPredicate.test(e)) {
					log.warn("Exceptions cannot be retried and are thrown immediately: {}", e.getMessage());
					throw new RuntimeException("Model call failed (non-retryable exception)", e);
				}

				// Wait and try again
				if (currentDelay > 0) {
					try {
						log.info("Retry after {} ms", currentDelay);
						Thread.sleep(currentDelay);
					} catch (InterruptedException ie) {
						Thread.currentThread().interrupt();
						throw new RuntimeException("Retry interrupted", ie);
					}
				}

View on GitHub (pinned to f82da0b50f)