4141import org .springframework .web .client .ResponseErrorHandler ;
4242
4343/**
44- * {@link AutoConfiguration Auto-configuration} for AI Retry.
44+ * {@link AutoConfiguration Auto-configuration} for AI Retry. Provides beans for retry
45+ * template and response error handling. Handles transient and non-transient exceptions
46+ * based on HTTP status codes.
4547 *
46- * @author Christian Tzolov
48+ * Author: Christian Tzolov
4749 */
4850@ AutoConfiguration
4951@ ConditionalOnClass (RetryUtils .class )
@@ -63,9 +65,10 @@ public RetryTemplate retryTemplate(SpringAiRetryProperties properties) {
6365 .withListener (new RetryListener () {
6466
6567 @ Override
66- public <T extends Object , E extends Throwable > void onError (RetryContext context ,
67- RetryCallback <T , E > callback , Throwable throwable ) {
68- logger .warn ("Retry error. Retry count:" + context .getRetryCount (), throwable );
68+ public <T , E extends Throwable > void onError (RetryContext context , RetryCallback <T , E > callback ,
69+ Throwable throwable ) {
70+ logger .warn ("Retry error. Retry count: {}, Exception: {}" , context .getRetryCount (),
71+ throwable .getMessage (), throwable );
6972 }
7073 })
7174 .build ();
@@ -84,29 +87,35 @@ public boolean hasError(@NonNull ClientHttpResponse response) throws IOException
8487
8588 @ Override
8689 public void handleError (@ NonNull ClientHttpResponse response ) throws IOException {
87- if (response .getStatusCode ().isError ()) {
88- String error = StreamUtils .copyToString (response .getBody (), StandardCharsets .UTF_8 );
89- String message = String .format ("%s - %s" , response .getStatusCode ().value (), error );
90-
91- // Explicitly configured transient codes
92- if (properties .getOnHttpCodes ().contains (response .getStatusCode ().value ())) {
93- throw new TransientAiException (message );
94- }
95-
96- // onClientErrors - If true, do not throw a NonTransientAiException,
97- // and do not attempt retry for 4xx client error codes, false by
98- // default.
99- if (!properties .isOnClientErrors () && response .getStatusCode ().is4xxClientError ()) {
100- throw new NonTransientAiException (message );
101- }
102-
103- // Explicitly configured non-transient codes
104- if (!CollectionUtils .isEmpty (properties .getExcludeOnHttpCodes ())
105- && properties .getExcludeOnHttpCodes ().contains (response .getStatusCode ().value ())) {
106- throw new NonTransientAiException (message );
107- }
90+ if (!response .getStatusCode ().isError ()) {
91+ return ;
92+ }
93+
94+ String error = StreamUtils .copyToString (response .getBody (), StandardCharsets .UTF_8 );
95+ if (error == null || error .isEmpty ()) {
96+ error = "No response body available" ;
97+ }
98+
99+ String message = String .format ("HTTP %s - %s" , response .getStatusCode ().value (), error );
100+
101+ // Explicitly configured transient codes
102+ if (properties .getOnHttpCodes ().contains (response .getStatusCode ().value ())) {
108103 throw new TransientAiException (message );
109104 }
105+
106+ // Handle client errors (4xx)
107+ if (!properties .isOnClientErrors () && response .getStatusCode ().is4xxClientError ()) {
108+ throw new NonTransientAiException (message );
109+ }
110+
111+ // Explicitly configured non-transient codes
112+ if (!CollectionUtils .isEmpty (properties .getExcludeOnHttpCodes ())
113+ && properties .getExcludeOnHttpCodes ().contains (response .getStatusCode ().value ())) {
114+ throw new NonTransientAiException (message );
115+ }
116+
117+ // Default to transient exception
118+ throw new TransientAiException (message );
110119 }
111120 };
112121 }
0 commit comments