@@ -117,6 +117,17 @@ class _ServiceExtensionTracker {
117117 }
118118}
119119
120+ /// Exception thrown when no browser clients are connected to DWDS.
121+ class NoClientsAvailableException implements Exception {
122+ final String message;
123+ final String operation;
124+
125+ NoClientsAvailableException (this .message, {required this .operation});
126+
127+ @override
128+ String toString () => 'NoClientsAvailableException: $message ' ;
129+ }
130+
120131/// WebSocket-based VM service proxy for web debugging.
121132class WebSocketProxyService extends ProxyService {
122133 final _logger = Logger ('WebSocketProxyService' );
@@ -504,6 +515,14 @@ class WebSocketProxyService extends ProxyService {
504515 await _performWebSocketHotReload ();
505516 _logger.info ('Hot reload completed successfully' );
506517 return _ReloadReportWithMetadata (success: true );
518+ } on NoClientsAvailableException catch (e) {
519+ // Gracefully handle no clients scenario
520+ _logger.info ('No clients available for hot reload' );
521+ return _ReloadReportWithMetadata (
522+ success: false ,
523+ notices: [e.message],
524+ noClientsAvailable: true ,
525+ );
507526 } catch (e) {
508527 _logger.warning ('Hot reload failed: $e ' );
509528 return _ReloadReportWithMetadata (success: false , notices: [e.toString ()]);
@@ -518,6 +537,16 @@ class WebSocketProxyService extends ProxyService {
518537 await _performWebSocketHotRestart ();
519538 _logger.info ('Hot restart completed successfully' );
520539 return {'result' : vm_service.Success ().toJson ()};
540+ } on NoClientsAvailableException catch (e) {
541+ // Return structured response indicating no clients available
542+ _logger.info ('No clients available for hot restart' );
543+ return {
544+ 'result' : {
545+ 'type' : 'Success' ,
546+ 'noClientsAvailable' : true ,
547+ 'message' : e.message,
548+ },
549+ };
521550 } catch (e) {
522551 _logger.warning ('Hot restart failed: $e ' );
523552 return {
@@ -611,7 +640,11 @@ class WebSocketProxyService extends ProxyService {
611640 });
612641
613642 if (clientCount == 0 ) {
614- throw StateError ('No clients available for hot reload' );
643+ _logger.warning ('No clients available for hot reload' );
644+ throw NoClientsAvailableException (
645+ 'No clients available for hot reload' ,
646+ operation: 'hot reload' ,
647+ );
615648 }
616649
617650 // Create tracker for this hot reload request
@@ -671,7 +704,11 @@ class WebSocketProxyService extends ProxyService {
671704 });
672705
673706 if (clientCount == 0 ) {
674- throw StateError ('No clients available for hot restart' );
707+ _logger.warning ('No clients available for hot restart' );
708+ throw NoClientsAvailableException (
709+ 'No clients available for hot restart' ,
710+ operation: 'hot restart' ,
711+ );
675712 }
676713
677714 // Create tracker for this hot restart request
@@ -737,9 +774,8 @@ class WebSocketProxyService extends ProxyService {
737774 final request = ServiceExtensionRequest .fromArgs (
738775 id: requestId,
739776 method: method,
740- args: args != null
741- ? Map <String , dynamic >.from (args)
742- : < String , dynamic > {},
777+ args:
778+ args != null ? Map <String , dynamic >.from (args) : < String , dynamic > {},
743779 );
744780
745781 // Send the request and get the number of connected clients
@@ -940,8 +976,8 @@ class WebSocketProxyService extends ProxyService {
940976 /// Pauses execution of the isolate.
941977 @override
942978 Future <Success > pause (String isolateId) =>
943- // Can't pause with the web socket implementation, so do nothing.
944- Future .value (Success ());
979+ // Can't pause with the web socket implementation, so do nothing.
980+ Future .value (Success ());
945981
946982 /// Resumes execution of the isolate.
947983 @override
@@ -1049,13 +1085,20 @@ class WebSocketProxyService extends ProxyService {
10491085/// Extended ReloadReport that includes additional metadata in JSON output.
10501086class _ReloadReportWithMetadata extends vm_service.ReloadReport {
10511087 final List <String >? notices;
1052- _ReloadReportWithMetadata ({super .success, this .notices});
1088+ final bool noClientsAvailable;
1089+
1090+ _ReloadReportWithMetadata ({
1091+ super .success,
1092+ this .notices,
1093+ this .noClientsAvailable = false ,
1094+ });
10531095
10541096 @override
10551097 Map <String , dynamic > toJson () {
10561098 final jsonified = < String , Object ? > {
10571099 'type' : 'ReloadReport' ,
10581100 'success' : success ?? false ,
1101+ 'noClientsAvailable' : noClientsAvailable,
10591102 };
10601103 if (notices != null ) {
10611104 jsonified['notices' ] = notices! .map ((e) => {'message' : e}).toList ();
0 commit comments