CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-springframework-ai--spring-ai-retry

Spring AI utility library providing retry mechanisms for AI API interactions with comprehensive error handling and exception classification

Overview
Eval results
Files

quick-start.mddocs/guides/

Quick Start Guide

This guide walks you through setting up and using Spring AI Retry in your application.

Installation

Maven

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>

Option 1: Using Static Constants (Simple Setup)

For quick setup or non-Spring Boot applications, use the pre-configured static constants.

Step 1: Import Required Classes

import org.springframework.ai.retry.RetryUtils;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestTemplate;

Step 2: Configure HTTP Client

RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER);

Step 3: Use Retry Template

RetryTemplate retryTemplate = RetryUtils.DEFAULT_RETRY_TEMPLATE;

String result = retryTemplate.execute(context -> {
    return restTemplate.postForObject(
        "https://api.example.com/chat",
        new ChatRequest(prompt),
        String.class
    );
});

Complete Example

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
            );
        });
    }
}

Option 2: Using Spring Boot Auto-Configuration (Recommended)

For Spring Boot applications, use auto-configuration for maximum flexibility.

Step 1: Add Dependency

Ensure spring-ai-autoconfigure-retry is in your pom.xml (see Installation section above).

Step 2: Configure Properties

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: 30000ms

Or 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=30000ms

Step 3: Inject Auto-Configured Beans

import 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
            );
        });
    }
}

Complete Spring Boot Example

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();
            }
        );
    }
}

Understanding Retry Behavior

What Gets Retried?

  • 5xx Server Errors: 500, 502, 503, 504 → Automatically retried
  • Network Errors: Connection timeouts, DNS issues → Automatically retried
  • TransientAiException: Any code throwing this → Automatically retried

What Doesn't Get Retried?

  • 4xx Client Errors: 400, 401, 403, 404, 429 → Not retried (by default)
  • NonTransientAiException: Any code throwing this → Not retried

Customizing Retry Behavior

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 Implemented

Error Handling

Basic Error Handling

import 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);
}

With Recovery Callback

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);
    }
);

Testing with SHORT_RETRY_TEMPLATE

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());
}

Next Steps

  • Configuration Guide - Learn about advanced configuration options
  • Integration Patterns - See real-world usage patterns
  • API Reference - Explore the complete API

Install with Tessl CLI

npx tessl i tessl/maven-org-springframework-ai--spring-ai-retry@1.1.0

docs

index.md

tile.json