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

  1. Let the retry template handle it — tune spring.ai.retry.backoff properties (initial/interval/multiplier/max-attempts) for your provider's rate limits.
  2. Reduce request rate or batch size if 429s are frequent.
  3. Remove rarely-retryable codes from spring.ai.retry.on-http-codes if you prefer to fail fast on them.
  4. 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

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


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/0319b8d346f60407. Report an issue: GitHub.