Spring AI utility library providing retry mechanisms for AI API interactions with comprehensive error handling and exception classification
This guide walks you through setting up and using Spring AI Retry in your application.
Add the dependency to your pom.xml:
<!-- Core module (manual configuration) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-retry</artifactId>
<version>1.1.2</version>
</dependency>
<!-- Or Spring Boot auto-configuration module -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-retry</artifactId>
<version>1.1.2</version>
</dependency>For quick setup or non-Spring Boot applications, use the pre-configured static constants.
import org.springframework.ai.retry.RetryUtils;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestTemplate;RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER);RetryTemplate retryTemplate = RetryUtils.DEFAULT_RETRY_TEMPLATE;
String result = retryTemplate.execute(context -> {
return restTemplate.postForObject(
"https://api.example.com/chat",
new ChatRequest(prompt),
String.class
);
});import org.springframework.ai.retry.RetryUtils;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestTemplate;
public class SimpleAiClient {
private final RestTemplate restTemplate;
private final RetryTemplate retryTemplate;
public SimpleAiClient() {
this.restTemplate = new RestTemplate();
this.restTemplate.setErrorHandler(RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER);
this.retryTemplate = RetryUtils.DEFAULT_RETRY_TEMPLATE;
}
public String generateText(String prompt) {
return retryTemplate.execute(context -> {
ChatRequest request = new ChatRequest(prompt);
return restTemplate.postForObject(
"https://api.example.com/chat",
request,
String.class
);
});
}
}For Spring Boot applications, use auto-configuration for maximum flexibility.
Ensure spring-ai-autoconfigure-retry is in your pom.xml (see Installation section above).
Create or update application.yml:
spring:
ai:
retry:
max-attempts: 5
on-http-codes: [429, 503, 504]
exclude-on-http-codes: [401, 403]
backoff:
initial-interval: 1000ms
multiplier: 2
max-interval: 30000msOr application.properties:
spring.ai.retry.max-attempts=5
spring.ai.retry.on-http-codes=429,503,504
spring.ai.retry.exclude-on-http-codes=401,403
spring.ai.retry.backoff.initial-interval=1000ms
spring.ai.retry.backoff.multiplier=2
spring.ai.retry.backoff.max-interval=30000msimport org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import org.springframework.stereotype.Service;
@Service
public class AiService {
private final RetryTemplate retryTemplate;
private final RestTemplate restTemplate;
@Autowired
public AiService(RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler) {
this.retryTemplate = retryTemplate;
this.restTemplate = new RestTemplate();
this.restTemplate.setErrorHandler(responseErrorHandler);
}
public String generateText(String prompt) {
return retryTemplate.execute(context -> {
ChatRequest request = new ChatRequest(prompt);
return restTemplate.postForObject(
"https://api.example.com/chat",
request,
String.class
);
});
}
}import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
@Service
public class AiChatService {
private final RetryTemplate retryTemplate;
private final RestTemplate restTemplate;
private final String apiUrl;
@Autowired
public AiChatService(
RetryTemplate retryTemplate,
ResponseErrorHandler responseErrorHandler,
@Value("${ai.api.url}") String apiUrl) {
this.retryTemplate = retryTemplate;
this.apiUrl = apiUrl;
this.restTemplate = new RestTemplate();
this.restTemplate.setErrorHandler(responseErrorHandler);
}
public ChatResponse chat(String message) {
return retryTemplate.execute(context -> {
ChatRequest request = ChatRequest.builder()
.message(message)
.model("gpt-4")
.build();
return restTemplate.postForObject(
apiUrl + "/chat",
request,
ChatResponse.class
);
});
}
public ChatResponse chatWithFallback(String message) {
return retryTemplate.execute(
// Main operation
context -> {
ChatRequest request = ChatRequest.builder()
.message(message)
.build();
return restTemplate.postForObject(
apiUrl + "/chat",
request,
ChatResponse.class
);
},
// Fallback on failure
recoveryContext -> {
return ChatResponse.builder()
.message("Service temporarily unavailable. Please try again later.")
.build();
}
);
}
}Override default behavior with configuration:
spring:
ai:
retry:
# Always retry rate limits (even though it's a 4xx)
on-http-codes: [429, 503, 504]
# Never retry these even if they're 5xx
exclude-on-http-codes: [501] # Not Implementedimport org.springframework.ai.retry.TransientAiException;
import org.springframework.ai.retry.NonTransientAiException;
try {
String result = retryTemplate.execute(context ->
aiClient.generate(prompt)
);
return result;
} catch (TransientAiException e) {
// All retries exhausted - service is down
logger.error("AI service unavailable after retries: {}", e.getMessage());
throw new ServiceUnavailableException("AI service temporarily down", e);
} catch (NonTransientAiException e) {
// Immediate failure - request is invalid
logger.error("Invalid AI request: {}", e.getMessage());
throw new BadRequestException("Unable to process request", e);
}String result = retryTemplate.execute(
// Main operation
context -> aiClient.generate(prompt),
// Recovery callback - called after all retries exhausted
recoveryContext -> {
logger.warn("Using cached response after retry exhaustion");
return cacheService.getLastKnownGood(prompt);
}
);For tests, use the faster retry template:
import org.springframework.ai.retry.RetryUtils;
import org.junit.jupiter.api.Test;
@Test
void testAiServiceWithRetry() {
// Use SHORT_RETRY_TEMPLATE for fast test execution
RetryTemplate testTemplate = RetryUtils.SHORT_RETRY_TEMPLATE;
AtomicInteger attempts = new AtomicInteger(0);
String result = testTemplate.execute(context -> {
int attempt = attempts.incrementAndGet();
if (attempt < 3) {
throw new TransientAiException("Temporary failure");
}
return "Success";
});
assertEquals("Success", result);
assertEquals(3, attempts.get());
}Install with Tessl CLI
npx tessl i tessl/maven-org-springframework-ai--spring-ai-retry@1.1.0