@@ -382,6 +382,7 @@ impl Repl {
382382 } ) ;
383383
384384 let client = self . client . clone ( ) ;
385+ let client_for_retry = client. clone ( ) ;
385386 let req = initial_request;
386387 let mut request_handle = runtime. spawn ( async move { client. create_message ( req) . await } ) ;
387388
@@ -408,7 +409,95 @@ impl Repl {
408409 return Ok ( ( ) ) ;
409410 }
410411
411- let response = response_result?;
412+ // Handle API errors, especially those related to invalid images
413+ let response = match response_result {
414+ Ok ( resp) => resp,
415+ Err ( e) => {
416+ // Check if this is an image-related API error
417+ if let SofosError :: Api ( ref msg) = e {
418+ let is_400_error = msg. contains ( "400" ) ;
419+ let is_image_error = msg. contains ( "Unable to download" )
420+ || msg. contains ( "invalid_request_error" )
421+ || msg. contains ( "verify the URL" ) ;
422+
423+ // Check if current message OR conversation has images
424+ let current_has_images = !image_refs. is_empty ( ) ;
425+ let conversation_has_images = self . session_state . conversation . messages ( )
426+ . iter ( )
427+ . any ( |msg| {
428+ use crate :: api:: { MessageContent , MessageContentBlock } ;
429+ if let MessageContent :: Blocks { content } = & msg. content {
430+ content. iter ( ) . any ( |block| matches ! ( block, MessageContentBlock :: Image { .. } ) )
431+ } else {
432+ false
433+ }
434+ } ) ;
435+
436+ let has_images = current_has_images || conversation_has_images;
437+
438+ if is_400_error && is_image_error && has_images {
439+ println ! (
440+ "\n {} {}\n " ,
441+ "⚠️ Image loading error:" . bright_yellow( ) . bold( ) ,
442+ "One or more image URLs in the conversation could not be loaded by the API"
443+ ) ;
444+
445+ self . session_state . conversation . remove_last_message ( ) ;
446+
447+ // Remove ALL images from conversation
448+ let messages = self . session_state . conversation . messages ( ) ;
449+ let mut cleaned_messages = Vec :: new ( ) ;
450+
451+ for msg in messages {
452+ use crate :: api:: { Message , MessageContent , MessageContentBlock } ;
453+ let cleaned_msg = match & msg. content {
454+ MessageContent :: Blocks { content } => {
455+ let filtered_blocks: Vec < MessageContentBlock > = content
456+ . iter ( )
457+ . filter ( |block| !matches ! ( block, MessageContentBlock :: Image { .. } ) )
458+ . cloned ( )
459+ . collect ( ) ;
460+
461+ if filtered_blocks. is_empty ( ) {
462+ continue ;
463+ } else {
464+ Message {
465+ role : msg. role . clone ( ) ,
466+ content : MessageContent :: Blocks { content : filtered_blocks } ,
467+ }
468+ }
469+ }
470+ _ => msg. clone ( ) ,
471+ } ;
472+ cleaned_messages. push ( cleaned_msg) ;
473+ }
474+
475+ self . session_state . conversation . clear ( ) ;
476+ self . session_state . conversation . restore_messages ( cleaned_messages) ;
477+
478+ let error_message = if !image_refs. is_empty ( ) {
479+ "[SYSTEM ERROR: Image URLs in your message could not be loaded and have been removed from the conversation.]"
480+ } else {
481+ "[SYSTEM ERROR: Image URLs from a previous message could not be loaded and have been removed from the conversation. You can continue normally.]"
482+ } . to_string ( ) ;
483+
484+ self . session_state . conversation . add_user_message ( error_message) ;
485+ let new_request = self . build_initial_request ( ) ;
486+
487+ println ! ( "{}" , "Retrying request without images..." . dimmed( ) ) ;
488+ println ! ( ) ;
489+
490+ runtime. block_on ( async {
491+ client_for_retry. create_message ( new_request) . await
492+ } ) ?
493+ } else {
494+ return Err ( e) ;
495+ }
496+ } else {
497+ return Err ( e) ;
498+ }
499+ }
500+ } ;
412501
413502 self . session_state
414503 . add_tokens ( response. usage . input_tokens , response. usage . output_tokens ) ;
0 commit comments