@@ -249,7 +249,7 @@ impl<T: HttpInfra> OpenAIResponsesProvider<T> {
249249 Err ( e) => Some ( Err ( e) ) ,
250250 }
251251 }
252- Err ( e) => Some ( Err ( anyhow :: anyhow! ( "SSE parse error: {}" , e) ) ) ,
252+ Err ( e) => Some ( Err ( into_sse_parse_error ( e) ) ) ,
253253 }
254254 } ) ;
255255
@@ -259,6 +259,20 @@ impl<T: HttpInfra> OpenAIResponsesProvider<T> {
259259 }
260260}
261261
262+ fn into_sse_parse_error < E > ( error : eventsource_stream:: EventStreamError < E > ) -> anyhow:: Error
263+ where
264+ E : std:: fmt:: Debug + std:: fmt:: Display + Send + Sync + ' static ,
265+ {
266+ let is_retryable = matches ! ( & error, eventsource_stream:: EventStreamError :: Transport ( _) ) ;
267+ let error = anyhow:: anyhow!( "SSE parse error: {}" , error) ;
268+
269+ if is_retryable {
270+ forge_domain:: Error :: Retryable ( error) . into ( )
271+ } else {
272+ error
273+ }
274+ }
275+
262276/// Derives an API base URL suitable for OpenAI Responses API from a configured
263277/// endpoint URL.
264278///
@@ -382,6 +396,12 @@ mod tests {
382396 use super :: * ;
383397 use crate :: provider:: mock_server:: MockServer ;
384398
399+ fn is_retryable ( error : & anyhow:: Error ) -> bool {
400+ error
401+ . downcast_ref :: < forge_domain:: Error > ( )
402+ . is_some_and ( |error| matches ! ( error, forge_domain:: Error :: Retryable ( _) ) )
403+ }
404+
385405 fn make_credential ( provider_id : ProviderId , key : & str ) -> Option < forge_domain:: AuthCredential > {
386406 Some ( forge_domain:: AuthCredential {
387407 id : provider_id,
@@ -803,6 +823,33 @@ mod tests {
803823 assert_eq ! ( headers[ 1 ] . 1 , "value" ) ;
804824 }
805825
826+ #[ test]
827+ fn test_into_sse_parse_error_marks_transport_errors_retryable ( ) {
828+ let error = into_sse_parse_error ( eventsource_stream:: EventStreamError :: Transport (
829+ anyhow:: anyhow!( "error decoding response body" ) ,
830+ ) ) ;
831+
832+ assert ! ( is_retryable( & error) ) ;
833+ assert_eq ! (
834+ error. to_string( ) ,
835+ "SSE parse error: Transport error: error decoding response body"
836+ ) ;
837+ }
838+
839+ #[ test]
840+ fn test_into_sse_parse_error_keeps_utf8_errors_non_retryable ( ) {
841+ let error =
842+ into_sse_parse_error ( eventsource_stream:: EventStreamError :: < anyhow:: Error > :: Utf8 (
843+ String :: from_utf8 ( vec ! [ 0xFF ] ) . unwrap_err ( ) ,
844+ ) ) ;
845+
846+ assert ! ( !is_retryable( & error) ) ;
847+ assert_eq ! (
848+ error. to_string( ) ,
849+ "SSE parse error: UTF8 error: invalid utf-8 sequence of 1 bytes from index 0"
850+ ) ;
851+ }
852+
806853 #[ test]
807854 fn test_get_headers_without_credential ( ) {
808855 let provider = Provider {
0 commit comments