Spring AI utility library providing retry mechanisms for AI API interactions with comprehensive error handling and exception classification
Complete exception reference for Spring AI Retry.
java.lang.RuntimeException
├── org.springframework.ai.retry.TransientAiException
└── org.springframework.ai.retry.NonTransientAiExceptionException for transient failures that may succeed on retry.
public class TransientAiException extends RuntimeExceptionPackage: org.springframework.ai.retry
Extends: java.lang.RuntimeException
Purpose: Represents AI service errors that are temporary and may succeed when retried without intervention.
5xx Server Errors
Network Issues
Temporary Service States
Token Issues
public TransientAiException(String message)Creates a new transient exception with the specified message.
Parameters:
message - The error message describing the transient failureExample:
throw new TransientAiException("AI service temporarily unavailable");public TransientAiException(String message, Throwable cause)Creates a new transient exception with the specified message and cause.
Parameters:
message - The error message describing the transient failurecause - The underlying causeExample:
try {
response = httpClient.execute(request);
} catch (SocketTimeoutException e) {
throw new TransientAiException("Connection timeout to AI service", e);
}All methods from java.lang.RuntimeException:
getMessage() - Returns the detail messagegetCause() - Returns the cause or nullgetStackTrace() - Returns stack trace elementsprintStackTrace() - Prints stack tracetoString() - Returns string representationtry {
return aiClient.generate(prompt);
} catch (ServiceUnavailableException e) {
throw new TransientAiException("AI service unavailable", e);
} catch (RateLimitException e) {
throw new TransientAiException("Rate limit exceeded", e);
}try {
return restTemplate.postForObject(url, request, String.class);
} catch (ResourceAccessException e) {
// Already a transient exception in Spring, but can wrap
throw new TransientAiException("Network error calling AI service", e);
}if (circuitBreaker.isOpen()) {
throw new TransientAiException("Circuit breaker open - service recovering");
}Exception for permanent failures that should not be retried.
public class NonTransientAiException extends RuntimeExceptionPackage: org.springframework.ai.retry
Extends: java.lang.RuntimeException
Purpose: Represents AI service errors that are permanent and will not succeed on retry unless the underlying cause is corrected.
4xx Client Errors
Authentication/Authorization
Validation Errors
Resource Issues
Configuration Errors
public NonTransientAiException(String message)Creates a new non-transient exception with the specified message.
Parameters:
message - The error message describing the permanent failureExample:
throw new NonTransientAiException("Invalid API key");public NonTransientAiException(String message, Throwable cause)Creates a new non-transient exception with the specified message and cause.
Parameters:
message - The error message describing the permanent failurecause - The underlying causeExample:
try {
request = objectMapper.readValue(json, Request.class);
} catch (JsonParseException e) {
throw new NonTransientAiException("Invalid request format", e);
}All methods from java.lang.RuntimeException:
getMessage() - Returns the detail messagegetCause() - Returns the cause or nullgetStackTrace() - Returns stack trace elementsprintStackTrace() - Prints stack tracetoString() - Returns string representationif (prompt == null || prompt.isEmpty()) {
throw new NonTransientAiException("Prompt cannot be empty");
}
if (prompt.length() > MAX_PROMPT_LENGTH) {
throw new NonTransientAiException(
"Prompt exceeds maximum length of " + MAX_PROMPT_LENGTH);
}try {
return aiClient.generate(prompt);
} catch (AuthenticationException e) {
throw new NonTransientAiException("Invalid API credentials", e);
}if (apiKey == null) {
throw new NonTransientAiException(
"API key not configured - check application.properties");
}
if (!model.isSupported()) {
throw new NonTransientAiException(
"Unsupported model: " + model);
}Is this a temporary failure that might succeed on retry?
│
├─ YES → Use TransientAiException
│ │
│ ├─ Network timeout/connection error
│ ├─ 5xx server error
│ ├─ Service overload/rate limit (temporary)
│ ├─ Circuit breaker open
│ └─ Renewable token expiration
│
└─ NO → Use NonTransientAiException
│
├─ 4xx client error (except transient rate limits)
├─ Invalid API key/credentials
├─ Validation error
├─ Resource not found
├─ Configuration error
└─ Permanent quota exceeded| HTTP Status | Default Exception | Rationale |
|---|---|---|
| 400 Bad Request | NonTransientAiException | Malformed request won't fix itself |
| 401 Unauthorized | NonTransientAiException | Invalid credentials need fixing |
| 403 Forbidden | NonTransientAiException | Insufficient permissions |
| 404 Not Found | NonTransientAiException | Resource doesn't exist |
| 429 Too Many Requests | NonTransientAiException* | Usually rate limit (configure as transient if temporary) |
| 500 Internal Server Error | TransientAiException | Temporary server issue |
| 502 Bad Gateway | TransientAiException | Upstream service temporarily down |
| 503 Service Unavailable | TransientAiException | Service temporarily overloaded |
| 504 Gateway Timeout | TransientAiException | Upstream service timeout |
* Default is non-transient, but can be configured as transient via spring.ai.retry.on-http-codes: [429]
try {
result = retryTemplate.execute(context -> aiClient.generate(prompt));
} catch (TransientAiException e) {
// All retries exhausted - service is down
logger.error("AI service unavailable after retries", e);
throw new ServiceUnavailableException("AI service temporarily down", e);
} catch (NonTransientAiException e) {
// Immediate failure - request is invalid
logger.error("Invalid AI request", e);
throw new BadRequestException("Unable to process request", e);
}try {
result = retryTemplate.execute(
context -> aiClient.generate(prompt),
recoveryContext -> cacheService.getLastKnownGood(prompt)
);
} catch (NonTransientAiException e) {
// Non-transient errors bypass recovery
throw new BadRequestException("Invalid request", e);
}try {
result = retryTemplate.execute(context -> aiClient.generate(prompt));
} catch (TransientAiException e) {
if (fallbackEnabled) {
return fallbackService.generate(prompt);
} else {
throw new ServiceUnavailableException("Service down", e);
}
} catch (NonTransientAiException e) {
throw new BadRequestException("Invalid request", e);
}try {
return httpClient.execute(request);
} catch (SocketTimeoutException e) {
throw new TransientAiException("Connection timeout", e);
} catch (ConnectException e) {
throw new TransientAiException("Connection refused", e);
} catch (UnknownHostException e) {
throw new TransientAiException("DNS resolution failed", e);
}if (response.getStatusCode().is5xxServerError()) {
throw new TransientAiException(
"Server error: " + response.getStatusCode());
} else if (response.getStatusCode().is4xxClientError()) {
throw new NonTransientAiException(
"Client error: " + response.getStatusCode());
}if (result.isQuotaExceeded()) {
if (result.isTemporaryQuota()) {
throw new TransientAiException("Temporary quota exceeded");
} else {
throw new NonTransientAiException("Monthly quota exceeded");
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-springframework-ai--spring-ai-retry@1.1.0