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