@@ -427,7 +427,12 @@ fn validate_export_messages(messages: &[ExportMessage]) -> Result<()> {
427427
428428/// Convert an export message to a protocol event.
429429fn message_to_event ( message : & ExportMessage , turn_id : & mut u64 , cwd : & Path ) -> Result < Event > {
430- let event_msg = match message. role . as_str ( ) {
430+ let role = message. role . trim ( ) ;
431+ if role. is_empty ( ) {
432+ bail ! ( "Invalid message role: role cannot be empty or whitespace-only" ) ;
433+ }
434+
435+ let event_msg = match role {
431436 "user" => {
432437 * turn_id += 1 ;
433438 EventMsg :: UserMessage ( UserMessageEvent {
@@ -474,15 +479,10 @@ fn message_to_event(message: &ExportMessage, turn_id: &mut u64, cwd: &Path) -> R
474479 finish_reason : None ,
475480 } )
476481 }
477- other => {
478- // Unknown role, treat as assistant message
479- EventMsg :: AgentMessage ( AgentMessageEvent {
480- id : None ,
481- parent_id : None ,
482- message : format ! ( "[{other}] {}" , message. content) ,
483- finish_reason : None ,
484- } )
485- }
482+ other => bail ! (
483+ "Invalid message role '{}': expected one of user, assistant, tool, or system" ,
484+ other
485+ ) ,
486486 } ;
487487
488488 Ok ( Event {
@@ -545,6 +545,63 @@ mod tests {
545545 assert ! ( matches!( event. msg, EventMsg :: AgentMessage ( _) ) ) ;
546546 }
547547
548+ #[ test]
549+ fn test_message_to_event_rejects_empty_role ( ) {
550+ let mut turn_id = 0u64 ;
551+ let cwd = PathBuf :: from ( "/tmp" ) ;
552+ let msg = ExportMessage {
553+ role : "" . to_string ( ) ,
554+ content : "hello" . to_string ( ) ,
555+ tool_calls : None ,
556+ tool_call_id : None ,
557+ timestamp : None ,
558+ } ;
559+
560+ let err = message_to_event ( & msg, & mut turn_id, & cwd) . unwrap_err ( ) ;
561+ assert ! (
562+ err. to_string( )
563+ . contains( "role cannot be empty or whitespace-only" )
564+ ) ;
565+ }
566+
567+ #[ test]
568+ fn test_message_to_event_rejects_whitespace_only_role ( ) {
569+ let mut turn_id = 0u64 ;
570+ let cwd = PathBuf :: from ( "/tmp" ) ;
571+ let msg = ExportMessage {
572+ role : " " . to_string ( ) ,
573+ content : "hello" . to_string ( ) ,
574+ tool_calls : None ,
575+ tool_call_id : None ,
576+ timestamp : None ,
577+ } ;
578+
579+ let err = message_to_event ( & msg, & mut turn_id, & cwd) . unwrap_err ( ) ;
580+ assert ! (
581+ err. to_string( )
582+ . contains( "role cannot be empty or whitespace-only" )
583+ ) ;
584+ }
585+
586+ #[ test]
587+ fn test_message_to_event_rejects_unknown_role ( ) {
588+ let mut turn_id = 0u64 ;
589+ let cwd = PathBuf :: from ( "/tmp" ) ;
590+ let msg = ExportMessage {
591+ role : "developer" . to_string ( ) ,
592+ content : "hello" . to_string ( ) ,
593+ tool_calls : None ,
594+ tool_call_id : None ,
595+ timestamp : None ,
596+ } ;
597+
598+ let err = message_to_event ( & msg, & mut turn_id, & cwd) . unwrap_err ( ) ;
599+ assert ! (
600+ err. to_string( )
601+ . contains( "expected one of user, assistant, tool, or system" )
602+ ) ;
603+ }
604+
548605 #[ tokio:: test]
549606 async fn test_import_empty_source_validation ( ) {
550607 let cmd = ImportCommand {
0 commit comments