spring-projects/spring-ai · warning · TransientAiException
HTTP %s - %s
Error message
HTTP %s - %s
What it means
SpringAiRetryAutoConfiguration registers a ResponseErrorHandler that formats 'HTTP <status> - <detail>' for failing model API responses. When the returned status code is in spring.ai.retry.on-http-codes, it throws a TransientAiException so the retry template retries the request. This is the explicitly-configured transient error path.
Source
Thrown at auto-configurations/common/spring-ai-autoconfigure-retry/src/main/java/org/springframework/ai/retry/autoconfigure/SpringAiRetryAutoConfiguration.java:120
handleError(response);
}
@SuppressWarnings("removal")
public void handleError(ClientHttpResponse response) throws IOException {
if (!response.getStatusCode().isError()) {
return;
}
String error = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8);
if (error == null || error.isEmpty()) {
error = "No response body available";
}
String message = String.format("HTTP %s - %s", response.getStatusCode().value(), error);
// Explicitly configured transient codes
if (properties.getOnHttpCodes().contains(response.getStatusCode().value())) {
throw new TransientAiException(message);
}
// Handle client errors (4xx)
if (!properties.isOnClientErrors() && response.getStatusCode().is4xxClientError()) {
throw new NonTransientAiException(message);
}
// Explicitly configured non-transient codes
if (!CollectionUtils.isEmpty(properties.getExcludeOnHttpCodes())
&& properties.getExcludeOnHttpCodes().contains(response.getStatusCode().value())) {
throw new NonTransientAiException(message);
}
// Default to transient exception
throw new TransientAiException(message);
}
};
}View on GitHub (pinned to 98a7beda4f)
Solutions
- Let the retry template handle it — tune spring.ai.retry.backoff properties (initial/interval/multiplier/max-attempts) for your provider's rate limits.
- Reduce request rate or batch size if 429s are frequent.
- Remove rarely-retryable codes from spring.ai.retry.on-http-codes if you prefer to fail fast on them.
- Check the provider status page / API quota when persistent.
Example fix
# before: default retries exhausting on 429 # after spring.ai.retry.backoff.initial-interval=2000 spring.ai.retry.backoff.multiplier=3 spring.ai.retry.max-attempts=10
Defensive patterns
Strategy: retry
Try / catch
try {
response = chatClient.prompt().call();
} catch (TransientAiException e) {
// already retried by RetryTemplate; back off further or degrade gracefully
log.warn("Model API still failing after retries: {}", e.getMessage());
} Prevention
- Configure spring.ai.retry.backoff.* to match provider rate limits
- Monitor 429 rates and add client-side throttling
- Subscribe to provider status pages
- Set sensible max-attempts so callers fail fast enough
When it happens
Trigger: A model HTTP call returns a status code listed in spring.ai.retry.on-http-codes (defaults include 429, 500, 502, 503, 529) — handleError formats the message and throws TransientAiException.
Common situations: Rate limiting (429) from OpenAI/Anthropic, transient 5xx provider outages, proxy/gateway 502-503 errors during high traffic.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- %s - %s
- Retry error. Retry count:
- Stateless Streamable-Http prompt method must not declare par
- Tool call limit exceeded (dynamic message: tool name and lim
- Failed to initialize ChromaVectorStore
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/0319b8d346f60407.
Report an issue: GitHub.