Spring Boot auto-configuration for AI retry capabilities with exponential backoff and intelligent HTTP error handling
This guide walks you through setting up and using Spring AI Retry Auto Configuration in your Spring Boot application.
Add to your pom.xml:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-retry</artifactId>
<version>1.1.2</version>
</dependency>Add to your build.gradle:
implementation 'org.springframework.ai:spring-ai-autoconfigure-retry:1.1.2'Once the dependency is added, Spring Boot automatically configures:
No explicit configuration required! The beans are ready to use.
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Service;
@Service
public class AiService {
private final RetryTemplate retryTemplate;
// Constructor injection
public AiService(RetryTemplate retryTemplate) {
this.retryTemplate = retryTemplate;
}
public String callAiModel(String prompt) {
return retryTemplate.execute(context -> {
// Your AI API call here
return performAiApiCall(prompt);
});
}
private String performAiApiCall(String prompt) {
// Implementation
return "AI response";
}
}import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.stereotype.Service;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RestConfig {
@Bean
public RestTemplate aiRestTemplate(ResponseErrorHandler errorHandler) {
RestTemplate template = new RestTemplate();
// Auto-configured error handler
template.setErrorHandler(errorHandler);
return template;
}
}
@Service
public class AiService {
private final RetryTemplate retryTemplate;
private final RestTemplate restTemplate;
public AiService(RetryTemplate retryTemplate, RestTemplate aiRestTemplate) {
this.retryTemplate = retryTemplate;
this.restTemplate = aiRestTemplate;
}
public String getCompletion(String prompt) {
return retryTemplate.execute(context -> {
// RestTemplate uses auto-configured error handler
// Throws TransientAiException or NonTransientAiException
return restTemplate.postForObject(
"https://api.example.com/complete",
prompt,
String.class
);
});
}
}Create application.properties or application.yml to customize:
# Maximum retry attempts
spring.ai.retry.max-attempts=10
# HTTP codes to always retry
spring.ai.retry.on-http-codes=429,503
# HTTP codes to never retry
spring.ai.retry.exclude-on-http-codes=401,403
# Exponential backoff settings
spring.ai.retry.backoff.initial-interval=2s
spring.ai.retry.backoff.multiplier=5
spring.ai.retry.backoff.max-interval=3mspring:
ai:
retry:
max-attempts: 10
on-http-codes:
- 429
- 503
exclude-on-http-codes:
- 401
- 403
backoff:
initial-interval: 2s
multiplier: 5
max-interval: 3mimport org.springframework.ai.retry.TransientAiException;
import org.springframework.ai.retry.NonTransientAiException;
public String callWithErrorHandling(String prompt) {
try {
return retryTemplate.execute(context -> callApi(prompt));
} catch (TransientAiException e) {
// All retries exhausted - temporary issue
log.error("Service temporarily unavailable: {}", e.getMessage());
return "Service unavailable. Please try again later.";
} catch (NonTransientAiException e) {
// Permanent failure - configuration/auth issue
log.error("Service configuration error: {}", e.getMessage());
return "Service error. Please check your configuration.";
}
}public String callWithFallback(String prompt) {
return retryTemplate.execute(
// Retry callback
context -> callApi(prompt),
// Recovery callback - invoked when all retries fail
context -> {
log.warn("Using fallback after {} attempts", context.getRetryCount());
return "Fallback response - service temporarily unavailable";
}
);
}import org.springframework.boot.CommandLineRunner;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.stereotype.Component;
@Component
public class RetryConfigVerifier implements CommandLineRunner {
private final RetryTemplate retryTemplate;
private final ResponseErrorHandler errorHandler;
public RetryConfigVerifier(RetryTemplate retryTemplate,
ResponseErrorHandler errorHandler) {
this.retryTemplate = retryTemplate;
this.errorHandler = errorHandler;
}
@Override
public void run(String... args) {
System.out.println("✓ RetryTemplate bean loaded: " + retryTemplate);
System.out.println("✓ ResponseErrorHandler bean loaded: " + errorHandler);
}
}import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.ai.retry.TransientAiException;
import java.util.concurrent.atomic.AtomicInteger;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
class RetryTest {
@Autowired
private RetryTemplate retryTemplate;
@Test
void testRetryOnTransientError() {
AtomicInteger attempts = new AtomicInteger(0);
String result = retryTemplate.execute(context -> {
int attempt = attempts.incrementAndGet();
if (attempt < 3) {
throw new TransientAiException("Transient error");
}
return "success";
});
assertThat(result).isEqualTo("success");
assertThat(attempts.get()).isEqualTo(3);
}
}public String simpleRetry() {
return retryTemplate.execute(context -> callApi());
}public String retryWithContext() {
return retryTemplate.execute(context -> {
int attemptNumber = context.getRetryCount() + 1;
log.info("Attempt {} of {}", attemptNumber, maxAttempts);
return callApi();
});
}public String retryWithFallback() {
return retryTemplate.execute(
context -> callApi(),
context -> getFallbackResponse()
);
}public String customExceptionHandling() {
try {
return performOperation();
} catch (IOException e) {
// Network error - transient
throw new TransientAiException("Network error", e);
} catch (AuthException e) {
// Auth error - non-transient
throw new NonTransientAiException("Authentication failed", e);
}
}Symptom: NoSuchBeanDefinitionException for RetryTemplate
Solution: Ensure dependency is on classpath and RetryUtils class is available:
// Verify RetryUtils is available
import org.springframework.ai.retry.RetryUtils;Symptom: No retries happening on errors
Solution: Ensure you're throwing TransientAiException:
// Correct - will retry
throw new TransientAiException("Error");
// Incorrect - won't retry
throw new RuntimeException("Error");Symptom: Application hangs with long retry delays
Solution: Reduce max-attempts and backoff intervals:
spring.ai.retry.max-attempts=3
spring.ai.retry.backoff.max-interval=5s// Core classes
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
// Exceptions
import org.springframework.ai.retry.TransientAiException;
import org.springframework.ai.retry.NonTransientAiException;
// Configuration
import org.springframework.ai.retry.autoconfigure.SpringAiRetryProperties;
// Utilities
import org.springframework.ai.retry.RetryUtils;| Property | Default | Description |
|---|---|---|
| max-attempts | 10 | Total retry attempts |
| on-client-errors | false | Retry 4xx errors |
| initial-interval | 2s | First retry delay |
| multiplier | 5 | Backoff multiplier |
| max-interval | 3m | Max retry delay |
| Code | Behavior | Reason |
|---|---|---|
| 4xx | No retry | Client error |
| 5xx | Retry | Server error |
| 429 | Retry (if configured) | Rate limit |
| 401, 403 | No retry (if configured) | Auth error |
tessl i tessl/maven-org-springframework-ai--spring-ai-autoconfigure-retry@1.1.1