@@ -139,6 +139,15 @@ fn is_thread_settings_update_unsupported(source: &JSONRPCErrorError) -> bool {
139139 && source. message . contains ( THREAD_SETTINGS_UPDATE_METHOD ) )
140140}
141141
142+ fn thread_goal_request_error ( context : & ' static str , err : TypedRequestError ) -> color_eyre:: Report {
143+ match err {
144+ TypedRequestError :: Server { source, .. } => {
145+ color_eyre:: eyre:: eyre!( "{}" , source. message)
146+ }
147+ err => color_eyre:: eyre:: eyre!( "{context}: {err}" ) ,
148+ }
149+ }
150+
142151/// Data collected during the TUI bootstrap phase that the main event loop
143152/// needs to configure the UI, telemetry, and initial rate-limit prefetch.
144153///
@@ -798,7 +807,7 @@ impl AppServerSession {
798807 } ,
799808 } )
800809 . await
801- . wrap_err ( "thread/goal/get failed in TUI" )
810+ . map_err ( |err| thread_goal_request_error ( "thread/goal/get failed in TUI" , err ) )
802811 }
803812
804813 pub ( crate ) async fn thread_goal_set (
@@ -820,7 +829,7 @@ impl AppServerSession {
820829 } ,
821830 } )
822831 . await
823- . wrap_err ( "thread/goal/set failed in TUI" )
832+ . map_err ( |err| thread_goal_request_error ( "thread/goal/set failed in TUI" , err ) )
824833 }
825834
826835 pub ( crate ) async fn thread_goal_clear (
@@ -836,7 +845,7 @@ impl AppServerSession {
836845 } ,
837846 } )
838847 . await
839- . wrap_err ( "thread/goal/clear failed in TUI" )
848+ . map_err ( |err| thread_goal_request_error ( "thread/goal/clear failed in TUI" , err ) )
840849 }
841850
842851 pub ( crate ) async fn logout_account ( & mut self ) -> Result < ( ) > {
@@ -1740,6 +1749,36 @@ mod tests {
17401749 use pretty_assertions:: assert_eq;
17411750 use tempfile:: TempDir ;
17421751
1752+ #[ test]
1753+ fn thread_goal_request_error_surfaces_server_message ( ) {
1754+ let error = TypedRequestError :: Server {
1755+ method : "thread/goal/get" . to_string ( ) ,
1756+ source : JSONRPCErrorError {
1757+ code : -32600 ,
1758+ message : "ephemeral thread does not support goals: thr_123" . to_string ( ) ,
1759+ data : None ,
1760+ } ,
1761+ } ;
1762+
1763+ assert_eq ! (
1764+ thread_goal_request_error( "thread/goal/get failed in TUI" , error) . to_string( ) ,
1765+ "ephemeral thread does not support goals: thr_123"
1766+ ) ;
1767+ }
1768+
1769+ #[ test]
1770+ fn thread_goal_request_error_keeps_transport_context ( ) {
1771+ let error = TypedRequestError :: Transport {
1772+ method : "thread/goal/get" . to_string ( ) ,
1773+ source : std:: io:: Error :: new ( std:: io:: ErrorKind :: BrokenPipe , "broken pipe" ) ,
1774+ } ;
1775+
1776+ assert_eq ! (
1777+ thread_goal_request_error( "thread/goal/get failed in TUI" , error) . to_string( ) ,
1778+ "thread/goal/get failed in TUI: thread/goal/get transport error: broken pipe"
1779+ ) ;
1780+ }
1781+
17431782 async fn build_config ( temp_dir : & TempDir ) -> Config {
17441783 ConfigBuilder :: default ( )
17451784 . codex_home ( temp_dir. path ( ) . to_path_buf ( ) )
0 commit comments