@@ -216,14 +216,45 @@ impl SshConnection {
216216 ) ;
217217 self . state . startup_error = Some ( format ! (
218218 "The {} environment could not be started.\r \n \
219- Please contact the course staff and include error code {}.\r \n ",
219+ This may be a temporary issue — please try connecting again in a moment.\r \n \
220+ If the problem persists, contact the course staff and include error code {}.\r \n ",
220221 self . state. exercise_name, error_id
221222 ) ) ;
222223 None
223224 }
224225 }
225226 }
226227
228+ /// Deliver a UUID-tagged failure message to the client when a runtime
229+ /// container connection fails (after auth has already succeeded). Logs
230+ /// the underlying error server-side, writes a retry-recommending message
231+ /// to the channel's stderr, sets a non-zero exit status, and closes the
232+ /// channel. Always returns `()` for the caller to bubble up.
233+ fn emit_container_failure (
234+ & self ,
235+ channel_id : ChannelId ,
236+ session : & mut Session ,
237+ stage : & str ,
238+ err : & anyhow:: Error ,
239+ ) -> Result < ( ) , russh:: Error > {
240+ let error_id = Uuid :: new_v4 ( ) ;
241+ error ! (
242+ "{} container connection failed [error_id={}]: {:#}" ,
243+ stage, error_id, err
244+ ) ;
245+ let msg = format ! (
246+ "Failed to connect to your {} environment.\r \n \
247+ This may be a temporary issue — please try connecting again in a moment.\r \n \
248+ If the problem persists, contact the course staff and include error code {}.\r \n ",
249+ self . state. exercise_name, error_id
250+ ) ;
251+ session. channel_success ( channel_id) ?;
252+ session. extended_data ( channel_id, 1 , CryptoVec :: from_slice ( msg. as_bytes ( ) ) ) ?;
253+ session. exit_status_request ( channel_id, 1 ) ?;
254+ session. close ( channel_id) ?;
255+ Ok ( ( ) )
256+ }
257+
227258 /// If a startup error was recorded during auth, deliver it to the client
228259 /// on the given channel and close the channel with exit status 1.
229260 /// Returns `true` if an error was delivered.
@@ -568,14 +599,7 @@ impl server::Handler for SshConnection {
568599 {
569600 Ok ( f) => f,
570601 Err ( e) => {
571- let error_id = Uuid :: new_v4 ( ) ;
572- error ! ( "Failed to connect to container [error_id={}]: {}" , error_id, e) ;
573- let msg = format ! (
574- "Error: Internal error (id: {}). If this issue persists, please contact a supervisor and provide the error id.\r \n " ,
575- error_id
576- ) ;
577- session. data ( channel_id, CryptoVec :: from_slice ( msg. as_bytes ( ) ) ) ?;
578- session. close ( channel_id) ?;
602+ self . emit_container_failure ( channel_id, session, "shell" , & e) ?;
579603 return Ok ( ( ) ) ;
580604 }
581605 } ;
@@ -668,10 +692,7 @@ impl server::Handler for SshConnection {
668692 {
669693 Ok ( f) => f,
670694 Err ( e) => {
671- let error_id = Uuid :: new_v4 ( ) ;
672- error ! ( "Failed to connect to container [error_id={}]: {}" , error_id, e) ;
673- session. channel_failure ( channel_id) ?;
674- session. close ( channel_id) ?;
695+ self . emit_container_failure ( channel_id, session, "exec" , & e) ?;
675696 return Ok ( ( ) ) ;
676697 }
677698 } ;
@@ -743,10 +764,7 @@ impl server::Handler for SshConnection {
743764 {
744765 Ok ( f) => f,
745766 Err ( e) => {
746- let error_id = Uuid :: new_v4 ( ) ;
747- error ! ( "Failed to connect to container [error_id={}]: {}" , error_id, e) ;
748- session. channel_failure ( channel_id) ?;
749- session. close ( channel_id) ?;
767+ self . emit_container_failure ( channel_id, session, "subsystem" , & e) ?;
750768 return Ok ( ( ) ) ;
751769 }
752770 } ;
0 commit comments